簡體   English   中英

React:為什么子組件在 prop 更改時不更新

[英]React: why child component doesn't update when prop changes

為什么在下面的偽代碼示例中,當 Container 更改 foo.bar 時,Child 不重新呈現?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

即使我在修改 Container 中的值后調用forceUpdate() ,Child 仍然顯示舊值。

更新孩子的屬性“key”等於名稱。 每次密鑰更改時,組件都會重新渲染。

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}

因為如果父級的 props 發生變化,子級不會重新渲染,但是如果它的 STATE 發生變化:)

你展示的是這個: https : //facebook.github.io/react/tips/communicate-between-components.html

它將通過 props 將數據從父級傳遞給子級,但那里沒有重新渲染邏輯。

您需要為父級設置一些狀態,然后在父級更改狀態時重新渲染子級。 這可能會有所幫助。 https://facebook.github.io/react/tips/expose-component-functions.html

我有同樣的問題。 這是我的解決方案,我不確定這是好的做法,如果不是,請告訴我:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

UPD:現在你可以使用 React Hooks 做同樣的事情:(僅當組件是一個函數時)

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);

確認,添加一個 Key 有效。 我瀏覽了文檔以嘗試了解原因。

React 希望在創建子組件時高效。 如果它與另一個子組件相同,則不會呈現新組件,這會使頁面加載速度更快。

添加一個 Key 會強制 React 渲染一個新組件,從而重置該新組件的 State。

https://reactjs.org/docs/reconciliation.html#recursing-on-children

當從functionsuseState創建 React 組件時。

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

工作

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

確實有效(添加密鑰)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />

根據 React 哲學,組件不能改變它的 props。 它們應該從父級接收並且應該是不可變的。 只有父級可以更改其子級的道具。

關於狀態與道具的很好解釋

另外,請閱讀此線程為什么我不能在 react.js 中更新道具?

您應該使用setState函數。 如果沒有,無論您如何使用 forceUpdate,state 都不會保存您的更改。

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

您的代碼似乎無效。 我無法測試此代碼。

您一定使用過動態組件。

在這個代碼片段中,我們多次渲染子組件並傳遞key

  • 如果我們多次動態地渲染一個組件,那么 React 不會渲染該組件,直到它的鍵被更改。

如果我們使用setState方法更改檢查 在我們更改其key之前,它不會反映在 Child 組件中。 我們必須將其保存在 child 的 state 中,然后相應地更改它以呈現 child。

 class Parent extends Component { state = { checked: true } render() { return ( <div className="parent"> { [1, 2, 3].map( n => <div key={n}> <Child isChecked={this.state.checked} /> </div> ) } </div> ); } }

使用 setState 函數。 所以你可以這樣做

       this.setState({this.state.foo.bar:123}) 

在句柄事件方法中。

一旦狀態被更新,它就會觸發變化,並且會發生重新渲染。

如果 Child 不維護任何狀態並且只是渲染 props 然后從父級調用它,您可能應該將 Child 作為功能組件。 替代方法是您可以將鈎子與功能組件 (useState) 一起使用,這將導致無狀態組件重新渲染。

你也不應該改變 propas,因為它們是不可變的。 維護組件的狀態。

Child = ({bar}) => (bar);
export default function DataTable({ col, row }) {
  const [datatable, setDatatable] = React.useState({});
  useEffect(() => {
    setDatatable({
      columns: col,
      rows: row,
    });
  /// do any thing else 
  }, [row]);

  return (
    <MDBDataTableV5
      hover
      entriesOptions={[5, 20, 25]}
      entries={5}
      pagesAmount={4}
      data={datatable}
    />
  );
}

此示例使用useEffectprops更改時更改狀態。

我的案例涉及在 props 對象上有多個屬性,並且需要在更改其中任何一個時重新渲染 Child。 上面提供的解決方案是有效的,但是為每一個添加一個密鑰,每個都變得乏味和骯臟(想象一下有 15 個......)。 如果有人面臨這個問題 - 您可能會發現將 props 對象字符串化很有用:

<Child
    key={JSON.stringify(props)}
/>

這樣,props 上每個屬性的每次更改都會觸發 Child 組件的重新渲染。

希望對某人有所幫助。

我遇到了同樣的問題。 我有一個Tooltip這是接收組件showTooltip道具,我是更新有關Parent基於一個組成部分if條件下,它是在得到更新Parent組成部分,但Tooltip組件沒有渲染。

const Parent = () => {
   let showTooltip = false;
   if(....){ showTooltip = true; }
   return(
      <Tooltip showTooltip={showTooltip}></Tooltip>
   )
}

我犯的錯誤是將showTooltip聲明為 let 。

我意識到我做錯了什么,我違反了渲染工作原理,用鈎子代替它完成了這項工作。

const [showTooltip, setShowTooltip] =  React.useState<boolean>(false);

在子組件的 connect 方法的 mapStateToProps 中定義更改的道具。

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

就我而言,channelList 的頻道已更新,因此我在 mapStateToProps 中添加了 chanelList

就我而言,我正在更新傳遞給組件的loading狀態。 在 Button 內, props.loading按預期通過(從 false 切換到 true),但顯示微調器的三元沒有更新。

我嘗試添加一個鍵,添加一個使用 useEffect() 等更新的狀態,但其他答案都不起作用。

對我有用的是改變這個:

setLoading(true);
handleOtherCPUHeavyCode();

對此:

setLoading(true);
setTimeout(() => { handleOtherCPUHeavyCode() }, 1)

我認為這是因為handleOtherCPUHeavyCode的進程非常繁重和密集,因此應用程序凍結了handleOtherCPUHeavyCode一秒鍾。 添加 1ms 超時允許加載布爾值更新,然后繁重的代碼功能可以完成它的工作。

您可以使用componentWillReceiveProps

componentWillReceiveProps({bar}) {
    this.setState({...this.state, bar})
}

歸功於喬什·倫斯福德

遵守不變性

一個相當古老的問題,但它是一個常青問題,如果只有錯誤的答案和解決方法,它也不會變得更好。 子對象沒有更新的原因不是缺少key或缺少狀態,原因是你沒有遵守不變性原則。

react 的目標是讓應用程序更快、更靈敏、更易於維護等等,但你必須遵循一些原則。 React 節點僅在必要時重新渲染,即如果它們已更新。 React 如何知道組件是否已更新? 因為狀態變了。 現在不要把它和 setState 鈎子混在一起。 狀態是一個概念,每個組件都有它的狀態。 狀態是組件在給定時間點的外觀或行為。 如果您有一個靜態組件,那么您始終只有一種狀態,而不必處理它。 如果組件必須以某種方式改變,它的狀態就會改變。

現在反應非常具有描述性。 組件的狀態可以從一些不斷變化的信息中導出,並且這些信息可以存儲在組件外部或內部。 如果信息存儲在內部,那么這是組件必須跟蹤自己的一些信息,我們通常使用像 setState 這樣的鈎子來管理這些信息。 如果此信息存儲在我們的組件之外,那么它就會存儲在不同的組件內,並且該組件必須跟蹤它,即它們的狀態。 現在另一個組件可以通過 props 將它們的狀態傳遞給我們。

這意味着如果我們自己的管理狀態發生變化或者通過 props 傳入的信息發生變化,則反應重新渲染。 這是自然行為,您不必將道具數據傳輸到您自己的狀態。 現在是非常重要的一點:當信息發生變化時,react 如何知道? 簡單:是進行比較! 每當你設置一些狀態或給 react 一些它必須考慮的信息時,它會將新給出的信息與實際存儲的信息進行比較,如果它們不相同,react 將重新呈現該信息的所有依賴項。 在這方面不一樣意味着 javascript === 運算符。 也許你已經明白了。 讓我們看看這個:

 let a = 42; let b = a; console.log('is a the same as b?',a === b); // a and b are the same, right? --> true a += 5; // now let's change a console.log('is a still the same as b?',a === b); // --> false

我們正在創建一個值的實例,然后創建另一個實例,將第一個實例的值分配給第二個實例,然后更改第一個實例。 現在讓我們看一下對象的相同流程:

 let a = { num: 42}; let b = a; console.log('is a the same as b?',a === b); // a and b are the same, right? --> true a.num += 5; // now let's change a console.log('is a still the same as b?',a === b); // --> true
這次的不同之處在於,對象實際上是指向內存區域的指針,並且在斷言 b=a 的情況下,您將 b 設置為與指向完全相同對象的 a 相同的指針。 無論你在 a 中做什么,你的 a 指針或 b 指針都可以訪問。 您的線路:

 this.props.foo.bar = 123

實際上更新了“this”指向的內存中的一個值。 React 無法通過比較對象引用來識別此類更改。 您可以更改對象的內容一千次,並且引用將始終保持不變,並且 react 不會重新渲染依賴組件。 這就是為什么您必須將 react 中的所有變量視為不可變的。 要進行可檢測的更改,您需要一個不同的參考,並且您只能通過新對象獲得它。 因此,您不必更改您的對象,而是將其復制到一個新對象,然后您可以在將其移交以做出反應之前更改其中的某些值。 看:

 let a = {num: 42}; console.log('a looks like', a); let b = {...a}; console.log('b looks like', b); console.log('is a the same as b?', a === b); // --> false
展開運算符(帶有三個點的那個)或映射函數是將數據復制到新對象或數組的常用方法。

如果您遵守不變性,所有子節點都會使用新的道具數據更新。

如果道具是對象JSON.stringify(obj)並將其設置為功能組件的key ,我有同樣的問題重新渲染對象道具。 只為反應key設置一個id對我不起作用。 奇怪的是,要更新組件,您必須在key上包含所有對象屬性並將其連接起來。

function Child(props) {
  const [thing, setThing] = useState(props.something)
  
  return (
   <>
     <div>{thing.a}</div>
     <div>{thing.b}</div>
   </>
  )
}

...

function Caller() {
   const thing = [{a: 1, b: 2}, {a: 3, b: 4}]
   thing.map(t => (
     <Child key={JSON.stringify(t)} something={thing} />
   ))
}

現在,只要thing對象在運行時更改它的值, Child組件就會正確地重新渲染它。

考慮到涉及 props 的渲染限制和我們對狀態的好處,如果你使用反應鈎子,你可以使用一些技巧。 例如,您可以使用 useEffect 手動將道具轉換為狀態。 它可能不應該是最佳實踐,但在這些情況下它會有所幫助。

import { isEqual } from 'lodash';
import { useEffect, useState } from 'react';

export const MyComponent = (props: { users: [] }) => {
  const [usersState, setUsersState] = useState([]);

  useEffect(() => {
    if (!isEqual(props.users, usersState)) {
      setUsersState(props.users);
    }
  }, [props.users]);

  <OtherComponent users={usersState} />;
};

安裝包 npm install react-native-uuid 或 yarn add react-native-uuid

從'react-native-uuid'導入導入uuid;

將此方法用於子級的 uuid 將解決重新渲染問題 uuid.v4();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM