【问题标题】:React is not updating props of a component passed as propsReact 不会更新作为 props 传递的组件的 props
【发布时间】:2019-09-11 03:05:00
【问题描述】:

我有一个看起来像这样的 React DOM:

MarketDepthWindow 的状态下,我有一个isBackgroundEnabled,我想将它作为道具传递给AskOrdersTab。但它只传递初始道具而不是更新。它应该在MarketDepthHeaderComponent 的按钮单击时更新。

我认为问题在于我将它作为道具传递给TabsContainer

我尝试将React.Component 更改为React.PureComponent,使用shouldComponentUpdate(),添加React.cloneElement 并通过TabsContainer 传递道具,但对我没有任何作用。

也许我犯了某种架构错误?也许我应该以某种方式使用 refs?

class AskOrdersTab extends React.Component {
  render() {
    return <div>bids</div>
  }
}

class BidOrdersTab extends React.Component {
  render() {
    return <div>asks</div>
  }
}

class MarketDepthHeaderComponent extends React.Component {
  render() {
    return <button class={this.props.isBackgroundEnabled ? 'active' : ''} onClick={this.props.handleBackgroundButtonClick}></button>
  }
}

class TabsContainer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      tabs: props.tabs.tabs || [],
      tabHeaderComponent: props.tabs.tabHeaderComponent || ''
    }
  }

  renderComponents() {
    let tabsComponents = [];
    for (let tab of this.state.tabs.length) {
      tabsComponents.push(
        <div>{tab.component}</div>
      )
    }
    return tabsComponents;
  }

  render() {
    return (
      <>
        {this.state.tabHeaderComponent}
        {this.renderComponents()}
      </>
    );
  }
}

class MarketDepthWindow extends React.Component {

  handleBackgroundButtonClick = (event) => {
    this.setState((prevState) => ({
      isBackgroundEnabled: !prevState.isBackgroundEnabled
    }))
  }

  constructor(props) {
    super(props)
    this.state = {
      isBackgroundEnabled: true,
      handleBackgroundButtonClick: this.handleBackgroundButtonClick
    }
  }

  render() {
    let tabsProps = {
      tabs: [
        { title: 'Bid', component: <BidOrdersTab {...this.state} /> },
        { title: 'Ask', component: <AskOrdersTab {...this.state} /> }
      ],
      tabHeaderComponent: <MarketDepthHeaderComponent {...this.state} />
    }
    return <TabsContainer tabs={tabsProps} isBackgroundEnabled={this.state.isBackgroundEnabled} />
  }
}

ReactDOM.render(
  <MarketDepthWindow />,
  document.getElementById("market-depth-window")
);

【问题讨论】:

  • 你的意思是它不会更新作为 prop 传递的组件的状态吗?
  • 听起来有点傻,但你不必将道具称为“this.props”,而不仅仅是道具,即使在构造函数中也是如此。现在想想有点奇怪。

标签: javascript reactjs react-props


【解决方案1】:

TabsContainer 中的状态是多余的,你应该避免在每个组件中存储状态,因为它会切断更新链,尝试使用 props 代替。

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

class AskOrdersTab extends React.Component {
  render() {
    return <div>bids</div>;
  }
}

class BidOrdersTab extends React.Component {
  render() {
    return <div>asks</div>;
  }
}

class MarketDepthHeaderComponent extends React.Component {
  render() {
    return (
      <button onClick={this.props.onClick}>
        {this.props.isBackgroundEnabled ? "enabled" : "disabled"}
      </button>
    );
  }
}

class TabsContainer extends React.Component {
  renderComponents() {
    let tabsComponents = [];
    for (let tab of this.props.tabs.tabs) {
      tabsComponents.push(<div key={tab.title}>{tab.component}</div>);
    }
    return tabsComponents;
  }

  render() {
    return (
      <>
        {this.props.tabs.tabHeaderComponent}
        {this.renderComponents()}
      </>
    );
  }
}

class MarketDepthWindow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isBackgroundEnabled: true
    };
  }

  handleBackgroundButtonClick = () => {
    this.setState({
      isBackgroundEnabled: !this.state.isBackgroundEnabled
    });
  };

  render() {
    let tabsProps = {
      tabs: [
        { title: "Bid", component: <BidOrdersTab {...this.state} /> },
        { title: "Ask", component: <AskOrdersTab {...this.state} /> }
      ],
      tabHeaderComponent: (
        <MarketDepthHeaderComponent
          onClick={this.handleBackgroundButtonClick}
          isBackgroundEnabled={this.state.isBackgroundEnabled}
        />
      )
    };
    return (
      <TabsContainer
        tabs={tabsProps}
        isBackgroundEnabled={this.state.isBackgroundEnabled}
      />
    );
  }
}

const rootElement = document.getElementById("market-depth-window");
ReactDOM.render(<MarketDepthWindow />, rootElement);

这是您的演示,您可以检查修复并确保其有效。祝你好运

【讨论】:

  • 非常感谢!事实上,错误在于我使用了 state 而不是 props。我删除了所有多余的状态,它起作用了!
【解决方案2】:

您的问题的简单答案是您将AskOrdersTab 存储在TabsContainer 的状态下,并且在您的情况下,您希望通过道具访问它。

解释:这导致每当您的组件更新时,TabsContainer 仍将呈现原始的AskOrdersTab(这是由于AskOrdersTab 处于状态并且当前版本更新了道具同一个对象)。当然,使用生命周期钩子可以解决这种行为,即 getDerivedStateFromProps。但在这种情况下,我不建议这样做。

TL;DR 用这个替换你的TabsContainer,它应该可以工作。

class TabsContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      tabHeaderComponent: props.tabs.tabHeaderComponent || '',
    };
  }

  renderComponents() {
    let tabsComponents = [];
    for (let tab of this.props.tabs.tabs) {
      tabsComponents.push(<div>{tab.component}</div>);
    }
    return tabsComponents;
  }

  render() {
    return (
      <>
        {this.state.tabHeaderComponent}
        {this.renderComponents()}
      </>
    );
  }
}

最后,我认为您在状态中存储了太多。尝试导入组件,或者看看两种常见的反应模式:

  • 高阶组件
  • 渲染道具

【讨论】:

  • 谢谢!我看着这些模式。现在一切都到位了。
猜你喜欢
  • 2018-04-06
  • 1970-01-01
  • 2018-07-31
  • 2019-09-15
  • 2021-10-09
  • 2020-02-15
  • 2019-06-04
  • 2015-08-24
  • 2017-11-17
相关资源
最近更新 更多