StrugglingCoder
StrugglingCoder

Reputation: 5021

How to call child component's method from a parent component in React

I have a Grid with 3*3 squares.

When a click on a square , we change the background color to green.

So, I tried to put the all the states in the parent GridContainer.

 state = {
    gridCells: []
  };

This will hold the indices that are clicked.

GridContainer nests Grid and Grid nests Square.

render() {
    return (
      <div>
        <Grid action={this.handler} />    
        <button>Reset Clicks</button>
      </div>
    );
  }

Here is my current implementation.

Now how do I clear the background cells when I reset clicks and make the background back to white again?

function Square(props) {
  const liClickHandler = event => {
    event.target.classList.add("highlight");
    props.clickAction();
  };
  return <li onClick={e => liClickHandler(e)} />;
}

function Grid(props) {
  const gridHandler = index => {
    props.action(index);
  };

  return (
    <ul>
      {Array.from(new Array(9)).map((item, index) => (
        <Square key={index} clickAction={() => gridHandler(index)} />
      ))}
    </ul>
  );
}

class GridContainer extends React.Component {
  state = {
    gridCells: []
  };

  handler = index => {
    let temp = [...this.state.gridCells];
    temp.push(index + 1);
    this.setState({
      gridCells: temp
    });
  };

  render() {
    return (
      <div>
        <Grid action={this.handler} />
        <button>Reset Clicks</button>
      </div>
    );
  }
}

So when I click a Sqaure , it calls a method clickAction that calls handler

that updates the state and we have an array which indices were clicked in order.

How do I implement Reset clicks that updates the background of those Sqaures back to white ? How do I let know my child know.

Am I maintaining the state wrong?

Sandbox link : https://codesandbox.io/s/3-x-3-grids-s0b43?file=/src/index.js:640-1563

Upvotes: 1

Views: 477

Answers (2)

Daniel
Daniel

Reputation: 1611

In react you should think the "react" way:

  • pass the necessary state down through the props
  • pass down the callbacks so that children can update the parent state

Here is corrected version of the demo:

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";
function Square(props) {
  return (
    <li onClick={props.onClick} className={props.active ? "highlight" : ""} />
  );
}

function Grid(props) {
  let squares = [];

  for (let i = 0; i < 9; i++) {
    squares.push(
      <Square
        key={i}
        onClick={() => props.onCellClick(i)}
        active={props.cells[i]}
      />
    );
  }

  return <ul>{squares}</ul>;
}

class GridContainer extends React.Component {
  state = {
    gridCells: []
  };

  onCellClick = index => {
    this.setState(prevState => {
      const newCells = [...prevState.gridCells];
      newCells[index] = true;
      return {
        gridCells: newCells
      };
    });
  };

  render() {
    return (
      <div>
        <Grid cells={this.state.gridCells} onCellClick={this.onCellClick} />

        <button
          onClick={() => {
            let that = this; //we could bind the callback aswell
            that.setState(() => ({ gridCells: [] }));
          }}
        >
          Reset Clicks
        </button>
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<GridContainer />, rootElement);

Upvotes: 2

sleepwalker
sleepwalker

Reputation: 1032

I'd advise to rethink the way how your components are structured.

Each component should be independent unit with it's own logic and state (if needed of course). I'm saying if needed for state, cause ideally components should be stateless.

There are several problems with Square class:

  1. It adds class via event.target, which is not react way to go. React works with virtual DOM and has it's own set of methods to interact with html. Working with DOM directly - will bite your later, starting from writing tests for your code in the future.
  2. It does not contain incoming information whether it should be highlighted or not

Both these problems result in fact that you cannot reset presentation of your squares easily.

I've updated your sample: https://codesandbox.io/s/3-x-3-grids-uflhr?file=/src/index.js

It's still not ideal, but you can notice that gridCells is passed from top via props. And then each square gets own props param. This allows state to come through the flow and let squares rerender with updated class.

Upvotes: 2

Related Questions