mbj
mbj

Reputation: 107

Passing state from child to parent in React; child has separate state

I have three components, from outermost to innermost: App => Welcome => SearchBar. There's a state defined in App and SearchBar, but what I want is to get the user-inputted data in SearchBar and display that in a "Results" page. As such, I'm trying to update the state in SearchBar and have that simultaneously update the state in App, so that I can pass that data on to another component that's a child of App (e.g. Results). I have the following code, but it's only updating the state in SearchBar and not that in App.

(I've looked at some examples where the child (in this case SearchBar) doesn't have its own state, but in this case I think it's necessary since I'm tracking user input. I may be wrong though.)

// App.js
export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: "" };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    event.preventDefault();
    this.setState({
      value: event.target.value
    });
  }

  render() {
    return (
      <Router>
        <div className="AppContainer">
          <Route
            exact
            path="/"
            render={props => <SearchBar handleSubmit={this.handleSubmit} />}
          />
...

// Welcome.js
export default class Welcome extends React.Component {
  render() {
    return (
      <div>
        <SearchBar handleSubmit={this.props.handleSubmit} />
      </div>
...

// SearchBar.js
export default class SearchBar extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: "" };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    event.preventDefault();
    this.setState({ value: event.target.value });
  }

  render() {
    return (
      <form onSubmit={this.props.handleSubmit}>
        <input
          type="text"
          placeholder="Search..."
          onChange={this.handleChange}
          value={this.state.value}
        />
        <input type="submit" />
      </form>
    );
  }
}

Then again, I'm quite new to React so this might not be a pattern that you're supposed to use. In any case, I would appreciate advice on how to best solve this.

Upvotes: 0

Views: 148

Answers (2)

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15688

Since you've already defined a handleSubmit() event-handler in App.js and passed it all the way down to your SearchBar.js component. You can extrapolate the data you need by giving the input tag in your SearchBar a name prop.

class Searchbar extends React.Component {
  state = {
    value: ""
  };

  handleOnChange = e => {
    this.setState({
      value: e.target.value
    });
  };

  render() {
    return (
      <form onSubmit={this.props.handleSubmit}>
        <input
          onChange={this.handleOnChange}
          value={this.state.value}
          name="search"
        />
      </form>
    );
  }
}

Then in App.js handleSubmit handler, target that name prop to get the value in the input.

  handleSubmit = e => {
    e.preventDefault();
    this.setState({
       value: e.target.search.value
    })
  };

This will likely have the least amount of re-renders.

Edit:

Yes we can totally display a new component upon submitting the form. We just need the help of a second state-value like displayResults or displayComponent then by using a simple if-check, we'll just toggle what components to show. See working example: Code Demo

Upvotes: 1

ravibagul91
ravibagul91

Reputation: 20755

In SearchBar component you should pass state value directly to handleSubmit function as,

<form onSubmit={(e)=>{e.preventDefault(); this.props.handleSubmit(this.state.value)}}>

In App component your handleSubmit function should be,

handleSubmit(inputValue) {
    this.setState({
      value: inputValue
    });
}

Demo

Upvotes: 1

Related Questions