反应:为什么道具更改时子组件不更新


135

为什么在以下伪代码示例中,当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>
  }
}

即使forceUpdate()在修改Container中的值后调用,Child仍会显示旧值。


5
这是您的代码吗?看来这不是有效的React代码

我认为props值不应在容器组件中更改,而应通过setState在父组件中更改,并且该状态应映射到容器props
Piyush Patel

使用这样的传播算子<Child bar = {... this.props.foo.bar} />
Sourav Singh

@AdrianWydmanski和其他5个投票的人:en.wikipedia.org/wiki/Pseudocode
David Newcomb

当组件重新就地渲染时,@ PiyushPatel道具会更新,如伪代码所示。这方面的另一个示例是使用<Route exact path="/user/:email" component={ListUserMessagePage} />,例如同一页面上的链接,它将在不创建新实例和运行常规生命周期事件的情况下更新道具。
David Newcomb

Answers:


94

更新子项以使其属性“键”等于名称。每次更改键时,组件都会重新渲染。

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

5
谢谢你 我使用的是redux存储,直到添加了密钥,我才能重新渲染组件。(我使用uuid库生成随机密钥,并且它起作用了)。
Maiya

使用钩子时,在现有阵列上设置状态时,我的孩子不会重新渲染。我的(可能是个坏主意)黑客是要同时设置一个虚拟道具的状态,并将其添加到useEffect依赖项数组:中 [props.notRenderingArr, props.dummy]。如果数组的长度总是变化,则可以将useEffect依赖项数组设置为[props.notRenderingArr.length]
骨病

5
这也是我所需要的。我很困惑,什么时候key需要,而仅仅是setState?我有一些孩子依靠父母的状态,但是他们并没有触发重新渲染……
dcsan

好答案。但是为什么会这样呢?
里沙夫

1
我使用索引作为键,但是它无法正常运行。现在我正在使用uuid,它非常完美:)感谢@Maiya
Ali Rehman

90

因为如果父项的props发生变化,但是STATE的状态发生变化,则子项不会重新呈现:)

您显示的是这样的:https : //facebook.github.io/react/tips/communicate-between-components.html

它将通过道具将数据从父级传递到子级,但是那里没有重新渲染逻辑。

您需要为父级设置一些状态,然后在父级更改状态下重新渲染子级。这可能会有所帮助。 https://facebook.github.io/react/tips/expose-component-functions.html


5
为什么setState会导致重新渲染,而forceUpdate不会呢?主题也很少,但是当从redux传递props并通过动作更新其状态时,如何更新组件?
Tuomas Toivonen

即使更新组件,传递的道具仍然存在,道具也不会更新。助焊剂是另一个话题,是:)
弗朗索瓦·理查德

3
状态!=道具,您需要阅读更多有关此的内容。您不会使用道具进行更新
弗朗索瓦·理查德

您可以直接在源代码中看到它,这根本就不是同一过程
Webwoman

所以您在这里所说的已经更改或者我没有正确理解,我对此提出了一个问题, stackoverflow.com
questions/58903921/…

50

我有同样的问题。这是我的解决方案,我不确定这是否是个好习惯,如果不是,请告诉我:

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

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

UPD:现在您可以使用React Hooks执行相同的操作:(仅当component是一个函数时)

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

3
这绝对是正确的答案,更不用说最简单的答案了
Guilherme Ferreira

1
我也正在使用这种方法。
原始

@ vancy-pants componentDidUpdate在这种情况下,出现在Child组件中。
尼克·弗里斯凯尔

4
您不需要也不应将道具转换为在子组件中声明的状态。只需直接使用道具即可。FunctionComponents如果道具发生变化,它将在其中自动更新子组件。
oemera

1
当您在从父级状态派生的子级组件中具有道具时,此方法也适用
Chefk5

10

根据React哲学的组成部分,无法更改其道具。他们应该从父母那里得到,并且应该是不变的。只有父母可以改变孩子的道具。

关于状态与道具的很好的解释

另外,请阅读此线程为什么我不能在react.js中更新道具?


这是否还意味着容器无法修改从Redux商店收到的道具?
Tuomas Toivonen

3
容器不直接从商店接收道具,而是通过读取redux状态树的一部分来提供道具(令人困惑的?)。困惑是因为我们不知道神奇的功能由react-redux连接。如果您看到connect方法的源代码,则基本上它会创建一个高阶组件,该组件的状态为storeState并已预订到redux存储。当storeState更改时,整个重新呈现都会发生,并且通过读取更改后的状态将修改后的props提供给容器。希望这能回答问题
Sujan Thakare

很好的解释,考虑到高阶分量可以帮助我理解正在发生的事情
Tuomas Toivonen

7

您应该使用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>
        }
    }
}

您的代码似乎无效。我无法测试此代码。


不是<Child bar={this.state.foo.bar} />吗?
Josh Broadhurst,

对。我更新答案。但是正如我所说,这可能不是有效的代码。只需从问题中复制即可。
JamesYin

5

functions和创建React组件时useState

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



0

如果不保持任何状态而只是渲染道具然后从父级调用它,则应该将Child用作功能组件。替代方法是,可以将钩子与功能组件(useState)一起使用,这将导致无状态组件重新呈现。

同样,您不应该更改道具,因为它们是不可变的。维护组件的状态。

Child = ({bar}) => (bar);

0

我遇到了同样的问题。我有一个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);

0

在子组件的connect方法的mapStateToProps中定义更改的道具。

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

export default connect(mapStateToProps)(ChannelItem);

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.