Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React - How change state of single component when there are multiple instance of same component?

I have 4 boxes with component name SingleTopicBox. I would like to change color of box when user clicks on particular box. SingleTopicBox component has props topicID as a unique identifier for each box. Currently when user clicks on any of the box it's changing state of all the boxes bgDisplayColor: 'red' however, I would like to only change the color of boxes that is clicked.

index.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>
    );
  }
};

topic-list.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>
    );
  }
};

Single-topic-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>
    );
  }
};
like image 874
Rahul Dagli Avatar asked Oct 11 '25 20:10

Rahul Dagli


1 Answers

You can't seem to decide whether you want the state for the top-level component or the SingleTopicBox, but you put it in the top-level component anyway.

You should make a choice: Should the SingleTopicBox component know it's been clicked or should MultiTopicsModuleIndex remember which SingleTopicBox has been clicked?

If you are ok with multiple SingleTopicBox component having the clicked state at the same time, you should move the state and the onClick hander to SingleTopicBox. You don't need to remember both if the component has been clicked and its background color. For example, you can:

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

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

then use this.state.bgDisplayColor in the render method

If you want only one component to be clicked at any single time, so that previously clicked component goes back to unclicked when another one is clicked, you may want to have the state and the handler live in the top-level component, as you already have it in your code. The only thing you need to remember in the state is topicID, then you pass it to the TopicsList component as property like this:

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

and in TopicsList you render something like this:

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>
    );
}
like image 157
Miloš Rašić Avatar answered Oct 14 '25 09:10

Miloš Rašić