React - 当同一组件有多个实例时,如何更改单个组件的状态?

IT技术 javascript css reactjs ecmascript-6
2021-05-03 02:01:27

我有 4 个带有组件名称的框SingleTopicBox当用户单击特定框时,我想更改框的颜色。SingleTopicBox组件有 props topicID 作为每个框的唯一标识符。目前,当用户单击任何一个框时,它会更改所有框的状态,bgDisplayColor: 'red'但是,我只想更改单击的框的颜色。

索引.jsx

import {React, ReactDOM} from '../../../../build/react';

import SelectedTopicPage from './selected-topic-page.jsx';
import TopicsList from './topic-list.jsx';
import topicPageData from '../../../content/json/topic-page-data.js';

export default class MultiTopicsModuleIndex extends React.Component {

  constructor(props) {
    super(props);
    this.setTopicClicked = this.setTopicClicked.bind(this);
    this.onClick = this.onClick.bind(this);
    () => topicPageData;
    this.state = {
      isTopicClicked: false,
      bgDisplayColor: 'blue'
    };
  };

  onClick(topicID) {
    this.setState({isTopicClicked: true, topicsID: topicID, bgDisplayColor: 'red'});
  };

  setTopicClicked(boolean) {
    this.setState({isTopicClicked: boolean});
  };

  render() {
    return (
      <div className="row">
        {this.state.isTopicClicked
          ? <SelectedTopicPage topicsVisited={this.topicsVisited} setTopicClicked={this.setTopicClicked} topicsID={this.state.topicsID} key={this.state.topicsID} topicPageData={topicPageData}/>
        : <TopicsList bgDisplayColor={this.state.bgDisplayColor} topicsVisited={this.topicsVisited} onClick={this.onClick}/>}
      </div>
    );
  }
};

主题列表.jsx

import {React, ReactDOM} from '../../../../build/react';

import SingleTopicBox from './single-topic-box.jsx';
import SelectedTopicPage from './selected-topic-page.jsx';

export default class TopicsList extends React.Component {
  render() {
    return (
      <div className="row topic-list">
        <SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="1" onClick={this.props.onClick} label="Topic"/>
        <SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="2" onClick={this.props.onClick} label="Topic"/>
        <SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="3" onClick={this.props.onClick} label="Topic"/>
        <SingleTopicBox bgDisplayColor={this.props.bgDisplayColor} topicID="4" onClick={this.props.onClick} label="Topic"/>
      </div>
    );
  }
};

单主题box.jsx

import {React, ReactDOM} from '../../../../build/react';

export default class SingleTopicBox extends React.Component {
  render() {
    var divStyle = {
      backgroundColor: this.props.bgDisplayColor
    };
    return (
      <div>
        <div className="col-sm-2">
          <div style={divStyle} className="single-topic" data-topic-id={this.props.topicID} onClick={() => this.props.onClick(this.props.topicID)}>
            {this.props.label}
            {this.props.topicID}
          </div>
        </div>
      </div>
    );
  }
};
1个回答

您似乎无法决定是需要顶级组件的状态还是 SingleTopicBox 的状态,但无论如何您都将其放在顶级组件中。

您应该做出一个选择:SingleTopicBox 组件应该知道它被点击了还是应该 MultiTopicsModuleIndex 记住哪个 SingleTopicBox 被点击了?

如果您可以同时处理多个具有单击状态的 SingleTopicBox 组件,您应该将状态和 onClick 处理程序移动到 SingleTopicBox。你不需要记住组件是否被点击和它的背景颜色。例如,您可以:

getInitialState: function() {
    return {
        bgDisplayColor: 'blue'
    };
},

onClick() {
    this.setState({bgDisplayColor: 'red'});
};

然后this.state.bgDisplayColor在渲染方法中使用

如果您只想在任何时候单击一个组件,以便先前单击的组件在单击另一个组件时返回未单击状态,您可能希望状态和处理程序位于顶级组件中,因为您已经这样做了把它放在你的代码中。在 state 中唯一需要记住的是 topicID,然后将它作为属性传递给 TopicsList 组件,如下所示:

<TopicsList clickedTopicID={this.state.topicID} onClick={this.onClick} />

并在 TopicsList 中呈现如下内容:

render: function() {
    var topics = [
        {id: 1, label: 'Topic'},
        {id: 2, label: 'Topic'},
        {id: 3, label: 'Topic'},
        {id: 4, label: 'Topic'},
    ];

    var boxes = [];

    for (var i = 0, len = topics.length; i < len; i++)
        boxes.push(<SingleTopicBox 
            bgDisplayColor={(this.props.clickedTopicID == topics[i].id) ? 'red' : 'blue'}
            topicID={topics[i].id}
            onClick={this.props.onClick}
            label={topics[i].label}
        />);

    return (
      <div className="row topic-list">
        {boxes}
      </div>
    );
}