简体   繁体   English

React:为什么子组件在 prop 更改时不更新

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

Why in the following pseudo-code example Child doesn't re-render when Container changes foo.bar?为什么在下面的伪代码示例中,当 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>
  }
}

Even if I call forceUpdate() after modifying the value in Container, Child still shows the old value.即使我在修改 Container 中的值后调用forceUpdate() ,Child 仍然显示旧值。

Update the child to have the attribute 'key' equal to the name.更新孩子的属性“key”等于名称。 The component will re-render every time the key changes.每次密钥更改时,组件都会重新渲染。

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

Because children do not rerender if the props of the parent change, but if its STATE changes :)因为如果父级的 props 发生变化,子级不会重新渲染,但是如果它的 STATE 发生变化:)

What you are showing is this: https://facebook.github.io/react/tips/communicate-between-components.html你展示的是这个: https : //facebook.github.io/react/tips/communicate-between-components.html

It will pass data from parent to child through props but there is no rerender logic there.它将通过 props 将数据从父级传递给子级,但那里没有重新渲染逻辑。

You need to set some state to the parent then rerender the child on parent change state.您需要为父级设置一些状态,然后在父级更改状态时重新渲染子级。 This could help.这可能会有所帮助。 https://facebook.github.io/react/tips/expose-component-functions.html https://facebook.github.io/react/tips/expose-component-functions.html

I had the same problem.我有同样的问题。 This is my solution, I'm not sure that is the good practice, tell me if not:这是我的解决方案,我不确定这是好的做法,如果不是,请告诉我:

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

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

UPD: Now you can do the same thing using React Hooks: (only if component is a function) UPD:现在你可以使用 React Hooks 做同样的事情:(仅当组件是一个函数时)

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

Confirmed, adding a Key works.确认,添加一个 Key 有效。 I went through the docs to try and understand why.我浏览了文档以尝试了解原因。

React wants to be efficient when creating child components. React 希望在创建子组件时高效。 It won't render a new component if it's the same as another child, which makes the page load faster.如果它与另一个子组件相同,则不会呈现新组件,这会使页面加载速度更快。

Adding a Key forces React to render a new component, thus resetting State for that new component.添加一个 Key 会强制 React 渲染一个新组件,从而重置该新组件的 State。

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

When create React components from functions and useState .当从functionsuseState创建 React 组件时。

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

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

This does not work工作

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

This does work (adding key)确实有效(添加密钥)

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

According to React philosophy component can't change its props.根据 React 哲学,组件不能改变它的 props。 they should be received from the parent and should be immutable.它们应该从父级接收并且应该是不可变的。 Only parent can change the props of its children.只有父级可以更改其子级的道具。

nice explanation on state vs props关于状态与道具的很好解释

also, read this thread Why can't I update props in react.js?另外,请阅读此线程为什么我不能在 react.js 中更新道具?

You should use setState function.您应该使用setState函数。 If not, state won't save your change, no matter how you use forceUpdate.如果没有,无论您如何使用 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>
        }
    }
}

Your code seems not valid.您的代码似乎无效。 I can not test this code.我无法测试此代码。

You must have used dynamic component.您一定使用过动态组件。

In this code snippet we are rendering child component multiple time and also passing key .在这个代码片段中,我们多次渲染子组件并传递key

  • If we render a component dynamically multiple time then React doesn't render that component until it's key gets changed.如果我们多次动态地渲染一个组件,那么 React 不会渲染该组件,直到它的键被更改。

If we change checked by using setState method.如果我们使用setState方法更改检查 It won't be reflected in Child component until we change its key .在我们更改其key之前,它不会反映在 Child 组件中。 We have to save that on child's state and then change it to render child accordingly.我们必须将其保存在 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> ); } }

Use the setState function.使用 setState 函数。 So you could do所以你可以这样做

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

inside the handle event method.在句柄事件方法中。

Once, the state is updated, it will trigger changes, and re-render will take place.一旦状态被更新,它就会触发变化,并且会发生重新渲染。

You should probably make the Child as functional component if it does not maintain any state and simply renders the props and then call it from the parent.如果 Child 不维护任何状态并且只是渲染 props 然后从父级调用它,您可能应该将 Child 作为功能组件。 Alternative to this is that you can use hooks with the functional component (useState) which will cause stateless component to re-render.替代方法是您可以将钩子与功能组件 (useState) 一起使用,这将导致无状态组件重新渲染。

Also you should not alter the propas as they are immutable.你也不应该改变 propas,因为它们是不可变的。 Maintain state of the component.维护组件的状态。

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}
    />
  );
}

this example use useEffect to change state when props change.此示例使用useEffectprops更改时更改状态。

My case involved having multiple properties on the props object, and the need to re-render the Child on changing any of them.我的案例涉及在 props 对象上有多个属性,并且需要在更改其中任何一个时重新渲染 Child。 The solutions offered above were working, yet adding a key to each an every one of them became tedious and dirty (imagine having 15...).上面提供的解决方案是有效的,但是为每一个添加一个密钥,每个都变得乏味和肮脏(想象一下有 15 个......)。 If anyone is facing this - you might find it useful to stringify the props object:如果有人面临这个问题 - 您可能会发现将 props 对象字符串化很有用:

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

This way every change on each one of the properties on props triggers a re-render of the Child component.这样,props 上每个属性的每次更改都会触发 Child 组件的重新渲染。

Hope that helped someone.希望对某人有所帮助。

I was encountering the same problem.我遇到了同样的问题。 I had a Tooltip component that was receiving showTooltip prop, that I was updating on Parent component based on an if condition, it was getting updated in Parent component but Tooltip component was not rendering.我有一个Tooltip这是接收组件showTooltip道具,我是更新有关Parent基于一个组成部分if条件下,它是在得到更新Parent组成部分,但Tooltip组件没有渲染。

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

The mistake I was doing is to declare showTooltip as a let.我犯的错误是将showTooltip声明为 let 。

I realized what I was doing wrong I was violating the principles of how rendering works, Replacing it with hooks did the job.我意识到我做错了什么,我违反了渲染工作原理,用钩子代替它完成了这项工作。

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

define changed props in mapStateToProps of connect method in child component.在子组件的 connect 方法的 mapStateToProps 中定义更改的道具。

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

export default connect(mapStateToProps)(ChannelItem);

In my case, channelList's channel is updated so I added chanelList in mapStateToProps就我而言,channelList 的频道已更新,因此我在 mapStateToProps 中添加了 chanelList

In my case I was updating a loading state that was passed down to a component.就我而言,我正在更新传递给组件的loading状态。 Within the Button the props.loading was coming through as expected (switching from false to true) but the ternary that showed a spinner wasn't updating.在 Button 内, props.loading按预期通过(从 false 切换到 true),但显示微调器的三元没有更新。

I tried adding a key, adding a state that updated with useEffect() etc but none of the other answers worked.我尝试添加一个键,添加一个使用 useEffect() 等更新的状态,但其他答案都不起作用。

What worked for me was changing this:对我有用的是改变这个:

setLoading(true);
handleOtherCPUHeavyCode();

To this:对此:

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

I assume it's because the process in handleOtherCPUHeavyCode is pretty heavy and intensive so the app freezes for a second or so.我认为这是因为handleOtherCPUHeavyCode的进程非常繁重和密集,因此应用程序冻结了handleOtherCPUHeavyCode一秒钟。 Adding the 1ms timeout allows the loading boolean to update and then the heavy code function can do it's work.添加 1ms 超时允许加载布尔值更新,然后繁重的代码功能可以完成它的工作。

You can use componentWillReceiveProps :您可以使用componentWillReceiveProps

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

Credit to Josh Lunsford 归功于乔什·伦斯福德

Obey immutability遵守不变性

Quite an old question but it's an evergreen problem and it doesn't get better if there are only wrong answers and workarounds.一个相当古老的问题,但它是一个常青问题,如果只有错误的答案和解决方法,它也不会变得更好。 The reason why the child object is not updating is not a missing key or a missing state, the reason is that you don't obey the principle of immutability.子对象没有更新的原因不是缺少key或缺少状态,原因是你没有遵守不变性原则。

It is the aim of react to make apps faster and more responsive and easier to maintain and so on but you have to follow some principles. react 的目标是让应用程序更快、更灵敏、更易于维护等等,但你必须遵循一些原则。 React nodes are only rerendered if it is necessary, ie if they have updated. React 节点仅在必要时重新渲染,即如果它们已更新。 How does react know if a component has updated? React 如何知道组件是否已更新? Because it state has changed.因为状态变了。 Now don't mix this up with the setState hook.现在不要把它和 setState 钩子混在一起。 State is a concept and every component has its state.状态是一个概念,每个组件都有它的状态。 State is the look or behaviour of the component at a given point in time.状态是组件在给定时间点的外观或行为。 If you have a static component you only have one state all the time and don't have to take care of it.如果您有一个静态组件,那么您始终只有一种状态,而不必处理它。 If the component has to change somehow its state is changing.如果组件必须以某种方式改变,它的状态就会改变。

Now react is very descriptive.现在反应非常具有描述性。 The state of a component can be derived from some changing information and this information can be stored outside of the component or inside.组件的状态可以从一些不断变化的信息中导出,并且这些信息可以存储在组件外部或内部。 If the information is stored inside than this is some information the component has to keep track itself and we normally use the hooks like setState to manage this information.如果信息存储在内部,那么这是组件必须跟踪自己的一些信息,我们通常使用像 setState 这样的钩子来管理这些信息。 If this information is stored outside of our component then it is stored inside of a different component and that one has to keep track of it, its theirs state.如果此信息存储在我们的组件之外,那么它就会存储在不同的组件内,并且该组件必须跟踪它,即它们的状态。 Now the other component can pass us their state thru the props.现在另一个组件可以通过 props 将它们的状态传递给我们。

That means react rerenders if our own managed state changes or if the information coming in via props changes.这意味着如果我们自己的管理状态发生变化或者通过 props 传入的信息发生变化,则反应重新渲染。 That is the natural behaviour and you don't have to transfer props data into your own state.这是自然行为,您不必将道具数据传输到您自己的状态。 Now comes the very important point: how does react know when information has changed?现在是非常重要的一点:当信息发生变化时,react 如何知道? Easy: is makes an comparison!简单:是进行比较! Whenever you set some state or give react some information it has to consider, it compares the newly given information with the actually stored information and if they are not the same, react will rerender all dependencies of that information.每当你设置一些状态或给 react 一些它必须考虑的信息时,它会将新给出的信息与实际存储的信息进行比较,如果它们不相同,react 将重新呈现该信息的所有依赖项。 Not the same in that aspect means a javascript === operator.在这方面不一样意味着 javascript === 运算符。 Maybe you got the point already.也许你已经明白了。 Let's look at this:让我们看看这个:

 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

We are creating an instance of a value, then create another instance, assign the value of the first instance to the second instance and then change the first instance.我们正在创建一个值的实例,然后创建另一个实例,将第一个实例的值分配给第二个实例,然后更改第一个实例。 Now let's look at the same flow with objects:现在让我们看一下对象的相同流程:

 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
The difference this time is that an object actually is a pointer to a memory area and with the assertion of b=a you set b to the same pointer as a leading to exactly the same object. 这次的不同之处在于,对象实际上是指向内存区域的指针,并且在断言 b=a 的情况下,您将 b 设置为与指向完全相同对象的 a 相同的指针。 Whatever you do in a can be accesed by your a pointer or your b pointer. 无论你在 a 中做什么,你的 a 指针或 b 指针都可以访问。 Your line: 您的线路:

 this.props.foo.bar = 123

actually updates a value in the memory where "this" is pointing at.实际上更新了“this”指向的内存中的一个值。 React simply can't recognize such alterations by comparing the object references. React 无法通过比较对象引用来识别此类更改。 You can change the contents of your object a thousand times and the reference will always stay the same and react won't do a rerender of the dependent components.您可以更改对象的内容一千次,并且引用将始终保持不变,并且 react 不会重新渲染依赖组件。 That is why you have to consider all variables in react as immutable.这就是为什么您必须将 react 中的所有变量视为不可变的。 To make a detectable change you need a different reference and you only get that with a new object.要进行可检测的更改,您需要一个不同的参考,并且您只能通过新对象获得它。 So instead of changing your object you have to copy it to a new one and then you can change some values in it before you hand it over to react.因此,您不必更改您的对象,而是将其复制到一个新对象,然后您可以在将其移交以做出反应之前更改其中的某些值。 Look:看:

 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
The spread operator (the one with the three dots) or the map-function are common ways to copy data to a new object or array. 展开运算符(带有三个点的那个)或映射函数是将数据复制到新对象或数组的常用方法。

If you obey immutability all child nodes update with new props data.如果您遵守不变性,所有子节点都会使用新的道具数据更新。

I have the same issue's re-rendering object props, if the props is an object JSON.stringify(obj) and set it as a key for Functional Components.如果道具是对象JSON.stringify(obj)并将其设置为功能组件的key ,我有同样的问题重新渲染对象道具。 Setting just an id on key for react hooks doesn't work for me.只为反应key设置一个id对我不起作用。 It's weird that to update the component's you have to include all the object properties on the key and concatenate it there.奇怪的是,要更新组件,您必须在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} />
   ))
}

Now anytime the thing object changes it's values on runtime, Child component will re-render it correctly.现在,只要thing对象在运行时更改它的值, Child组件就会正确地重新渲染它。

Considering the rendering limitations involving props and the gains we have with states, if you use reaction hooks, there are a few tricks you can use.考虑到涉及 props 的渲染限制和我们对状态的好处,如果你使用反应钩子,你可以使用一些技巧。 For example, you can convert props to state manually using useEffect.例如,您可以使用 useEffect 手动将道具转换为状态。 It probably shouldn't be the best practice, but it helps in theses cases.它可能不应该是最佳实践,但在这些情况下它会有所帮助。

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} />;
};

install package npm install react-native-uuid or yarn add react-native-uuid安装包 npm install react-native-uuid 或 yarn add react-native-uuid

import import uuid from 'react-native-uuid';从'react-native-uuid'导入导入uuid;

use this method for uuid for child will solve rerender issue uuid.v4();将此方法用于子级的 uuid 将解决重新渲染问题 uuid.v4();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么这个 React 组件在其 prop 更改时不重新渲染? - Why doesn't this React component re-render when its prop changes? 如何在父项更改时将道具传递给反应组件但不更新子项中的该道具? - How do I pass a prop to a react component yet not update that prop in the child when parent changes? 状态更改时,prop不会更新 - prop doesn't update when state changes 组件属性不会在嵌套对象更改时更新DOM - Component prop doesn't update DOM on nested object changes 用户更改输入时,React不会更新组件状态 - React doesn't update component state when user changes input 当 prop 更改时,React 功能组件不会重新渲染 - React functional component doesn't re-render when prop changes Vue 子组件不会在 Prop 更改时更新 - Vue Child Component Doesn't Update on Prop Change 单向数据流:父组件异步更新 prop 时子组件不更新 - One-Way Data Flow: Child component doesn't update when prop is asynchronously updated by parent React - 更改 key prop 不会导致子组件的重新渲染 - React - changing key prop doesn't cause rerender of child component React:如何有效地制作我的组件,以便在道具更改时整个组件不会重新渲染? - React: How can I efficiently make my component so the entire component doesn't re-render when a prop changes?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM