0

(Pardon the verbose question. I'm brand new to React and ES6, and I'm probably overly-convoluting this.)

I am writing an app that contains a button component. This button calls a method onAddChild that creates another component of class ColorModule by adding a value to an array stored in the App's state.

In each newly created ColorModule, I want to include another button that will remove the module. Since this component is created by an array.map method, my thought is that if I can find the index of the array item that corresponds with the component and use that index in array.splice then perhaps that component will be removed (untested theory). That said, I'm not really sure how to find the index where I would use this in my onRemoveModule method.

Two part question: 1) How would I go about finding the index of the array item in my state, and 2) if I'm completely off base or there's a better way to do this altogether, what does that solution look like?

imports...

class App extends Component {

  static propTypes = {
    children: PropTypes.node,
  };

  constructor(props) {
    super(props);
    this.state = {
      // Here's the array in question...
      moduleList: [1],
    };
    this.onAddChild = this.onAddChild.bind(this);
    this.onRemoveModule = this.onRemoveModule.bind(this);
    this.className = bemClassName.bind(null, this.constructor.name);
  }

  onAddChild(module) {
    const moduleList = this.state.moduleList;
    this.setState({ moduleList: moduleList.concat(1) });
  }

  onRemoveModule( e ) {
    e.preventDefault();
    ...¯\_(ツ)_/¯
  }

  render() {
    const { className } = this;

    return (
      <div className={className('container')}>
        <Header onAddChild={this.onAddChild} /> /* Add module button lives here */
        <div className="cf">
          {this.state.moduleList.map(
            ( delta, index ) => {
              return (
                <ColorModule
                  className="cf"
                  onRemove={this.onRemoveModule}
                  key={index}
                  moduleId={'colorModule' + index}
                />
              ); /* Remove module button would live in the module itself */
            }
          )}
        </div>
      </div>
    );
  }
}

export default App;
  • i will suggest that instead of storing 1 in moduleList array in state, put a unique id for identification of element and use that id as key, as well as pass that to child component, use that id to remove the component. – Mayank Shukla Jan 12 '18 at 06:14
  • @MayankShukla great advice -- done and thank you! – Chance Strickland Jan 12 '18 at 19:14

2 Answers2

1

Well this part is pretty easy, all you need to do is pass the index as prop to the ColorModule component and when calling the onRemove method in it you could pass it back to the onRemoveModule. However react optimizes based on keys and its a really good idea to have a unique id given to each module instance.

class App extends Component {

  static propTypes = {
    children: PropTypes.node,
  };

  constructor(props) {
    super(props);
    this.state = {
      // Here's the array in question...
      moduleList: [1],
    };
    this.onAddChild = this.onAddChild.bind(this);
    this.onRemoveModule = this.onRemoveModule.bind(this);
    this.className = bemClassName.bind(null, this.constructor.name);
  }

  onAddChild(module) {
    const moduleList = this.state.moduleList;
    this.setState({ moduleList: moduleList.concat(uuid()) }); //uuid must return a unique id everytime to be used as component key
  }

  onRemoveModule( index ) {
        // now with this index you can update the moduleList
  } 

  render() {
    const { className } = this;

    return (
      <div className="cf">
          {this.state.moduleList.map(
            ( delta, index ) => {
              return (
                <ColorModule
                  className="cf"
                  index={index}
                  onRemove={this.onRemoveModule}
                  key={delta}
                  moduleId={'colorModule' + delta}
                />
              ); 
            }
          )}
        </div>
    );
  }
}

Now in ColorModule component

class ColorModule extends React.Component {

     onRemoveClick=() => {
         this.props.onRemove(this.props.index);
     }

}

Check this answer for more details on how to pass data from Child component to Parent

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
0

I ended up solving this problem using some of the guidance here from @ShubhamKhatri (didn't know about unique ID generation!), but I took a slightly different approach and handled the solution using state manipulation in App without needing a new method in my ColorModule component. I also never knew about currying in ES6, so that discovery made passing in the index values needed to manipulate my state array possible

If I'm off-base here or being inefficient, I'm definitely still open to feedback on a better way!

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      moduleList: [{ id: UniqId(), removeModule: false }],
    };
    this.onAddChild = this.onAddChild.bind(this);
    this.className = bemClassName.bind(null, this.constructor.name);
  }

  onAddChild(module) {
    const moduleList = this.state.moduleList;
    this.setState({
      moduleList: moduleList.concat({
        id: UniqId(),
        removeModule: false,
      }),
    });
  }

  onRemoveModule = ( i, arr ) => (e) => {
    const moduleList = this.state.moduleList;
    e.preventDefault();
    moduleList[i].removeModule = true;
    this.setState({ moduleList: moduleList });
  }

  render() {
    const { className } = this;

    return (
      <div className={className('container')}>
        <Header onAddChild={this.onAddChild} />
        <div className="cf">
          {this.state.moduleList.map(
            ( delta, index ) => {
              if ( !this.state.moduleList[index].removeModule ) {
                return (
                  <ColorModule
                    className="cf"
                    onRemove={this.onRemoveModule( index, this.state.moduleList )}
                    index={index}
                    key={delta.id}
                    moduleId={'colorModule' + delta}
                  />
                );
              }
            }
          )}
        </div>
      </div>
    );
  }
}