简体   繁体   中英

React is not updating props of a component passed as props

I have a React DOM that looks like this:

DOM结构

In MarketDepthWindow 's state i have a isBackgroundEnabled which i want to pass as a props to the AskOrdersTab . But it passes only the initial props and not updating. It should update on MarketDepthHeaderComponent 's button click.

I assume that the problem is that i pass it as a prop to TabsContainer .

I tried changing React.Component to React.PureComponent , using shouldComponentUpdate() , adding React.cloneElement and passing props through the TabsContainer but nothing works for me.

Maybe I made some kind of architectural mistake? Maybe i should use refs in some way?

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")
);

The simple answer to your question is that your storing AskOrdersTab in the state of TabsContainer , and in your case you want to access it via props.

Explanation: What this has caused is whenever your component updates, which it is doing, TabsContainer will still render the original AskOrdersTab (this is due to the AskOrdersTab in state and the current version with updated props being the same object). There are ofcourse workarounds for this sort of behaviour using lifecycle hooks, namely getDerivedStateFromProps. But in this case I wouldn't advise that.

TL;DR replace your TabsContainer with this one and it should work.

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

Finally, I think you're storing too much in state. Try importing components, or take a look at two common react patterns:

  • Higher Order Components
  • Render Props

The state within TabsContainer is redundant, you should avoid storing state in each component as it cuts the update chain, Try using props instead.

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

This is your demo, you can check the fix and make sure it works. Good Luck

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM