Pass state from class component to function component in react

Viewed 3681

I have a class where I declare:

constructor() {
  super();
  this.state = {
    checked: false,
    house: [],
    selectedHouse: null
  };
  this.handleChange = this.handleChange.bind(this);
}

handleChange(checked) {
  this.setState({ checked });
}

render() {
  return (
    <React.Fragment>
        <TSwitch handleChange={this.handleChange.bind(this)} house={this.state.house} houseClicked={this.h}></TSwitch>
    </React.Fragment>

  );
}

I then want to set state.checked from a child component:

function TSwitch(props) {
  const handleChange = (house) => (evt) => {
    props.handleChange(house);
  };

  return (
    <div>
      {props.house.map((house) => {
        return (
          <label>
            <span>Switch with default style</span>
            <Switch onChange={handleChange} checked={this.state.checked} />
          </label>
        );
      })}
    </div>
  );
}

I am able to call handleChange but I want to be able to change the value of state.checked from the <TSwitch/> component.

3 Answers

This is what your parent component should be like:

constructor() {
  super();
  this.state = {
    checked: false,
    house: [],
    selectedHouse: null
  };
  this.handleChange = this.handleChange.bind(this);
}

handleChange(checked) {
  this.setState({ checked });
}

render() {
  return (
    <React.Fragment>
        <TSwitch handleChange={this.handleChange} isChecked={this.state.checked} house={this.state.house}></TSwitch>
    </React.Fragment>

  );
}

This is what your child component should look like:

function TSwitch(props) {

  return (
    <div>
      {props.house.map((house) => {
        return (
          <label>
            <span>Switch with default style</span>
            <Switch onChange={x => props.handleChange(x)} checked={props.isChecked} />
          </label>
        );
      })}
    </div>
  );
}

NOTE: You are using a Switch component, I'm not sure if the variable x will be a boolean or an object, but most probably it should be a boolean: true or false. If this doesn't work, log the value of x & see if its an object, and pass the boolean in props.handleChange. Although I still think this won't be needed. Good luck!

1.

Let's start with your direct question

I want to be able to change the value of state.checked from the <TSwitch/> component

1.1 You've correctly passed your mutator function handleChange from the Parent class to TSwitch but your abstraction function handleChange inside that child, that you've duplicated, is unnecessary and should be removed completely.

1.2 Next, going back to the class' handleChange function, you need to modify the handleChange function definition in the parent component, by fixing the argument you passed it -- which will be the event object, passed implicitly since you registered it as a callback to onChange={handleChange} inside Tswitch. At invocation time, it will be called, and the evt argument that's given to onChange from React, will be passed into handleChange. But, you don't need it. It carries no information of necessity to you. So I would ignore it entirely.

// @ parent component

handleChange(evt) {
  // NOTE: i'm going to ignore the evt, since I don't need it.

  // NOTE: i'm going to use optional callback given by setState, to access prevState, and toggle the checked state boolean value.
  this.setState((prevState) => ({ checked: !prevState.checked }));
}


2.

Now let's clean up your code and talk about some best practices

2.1 You dont' need to be using React.Fragment here. Why? because Fragments were introduced in React 16 to provide a declarative API for handling lists of components. Otherwise, they're unecessary abstractions. Meaning: if you're not directly dealing with sibling components, then you don't need to reach for React.Fragment just go with a <div/> instead; would be more idiomatic.

2.2. If <TSwitch></TSwitch> isn't going to have a direct descendent, then you should change your usage syntax to <TSwitch/>.

2.3 If 2.2 didnt' get picked up by a linter, then I highly advised you install one.

2.4 You can continue using explicit bindings of your class handlers in your constructor if you'd like. It's a good first step in learning React, however, there's optimal ways to remove this boilerplate via Babel's transform properties plugins.

This will work:

handleChange(checked) {
  this.setState({ checked:!checked });
}
Related