Search code examples
reactjsstatejsxreact-props

Props don't update when state updates in React


I'm passing some values from my state in the App component to the GetWordContainer component as props. With these props, I set the state in my child component. However, the state in GetWordContainer only updates once. How can I have the state in the child component continue to update when the state in App.js changes?

App.js

class App extends Component {

  state = {
    redirect: {},
    word: '',
    error: '',
    info: [],
    partOfSpeech: [],
    versions: [],
    shortdef: "",
    pronunciation: "",
  }

  setRedirect = redirect =>{{
    this.setState({redirect})
  }
  // console.log(this.state.redirect);
  console.log(this.state.word);
}
  

  handleUpdate = values => 
    this.setState({...values})
    


render() {
    return (
      <>
        <Route 
          render={ routeProps => <Redirector redirect={this.state.redirect} setRedirect={this.setRedirect} {...routeProps} />}
        />
        <header>
          <nav>
            <Link to='/'>My Dictionary</Link>
          </nav>
        </header>

        <main>
        <Route
            render = { routeProps =>
              !routeProps.location.state?.alert ? '' :
              <div>
                { routeProps.location.state.alert }
              </div>
            }
          />
          <Switch>
            <Route exact path="/" render={ routeProps => 
              <Home 
                setRedirect={this.setRedirect} 
                handleUpdate={this.handleUpdate} 
                {...routeProps} />} 
              />
            <Route exact path="/definition/:word" render={routeProps => 
              <GetWordContainer 
                setRedirect={this.setRedirect}
                handleUpdate={this.handleUpdate}
                word={this.state.word} 
                partOfSpeech={this.state.partOfSpeech}
                versions={this.state.versions}
                shortdef={this.state.shortdef}
                pronunciation={this.state.pronunciation}
                {...routeProps} />} 
              />
          </Switch>
        </main>
      </>
    );
  }
}

GetWordContainer.js

class GetWordContainer extends Component {

//this state only updates once 
state = {
  word: this.props.word,
  info: this.props.info,
  partOfSpeech: this.props.parOfSpeech,
  versions: this.props.versions,
  shortdef: this.props.shortdef,
  pronunciation: this.props.pronunciation,
}


render (){

  return (

      <div>
        <Search
          handleUpdate={this.props.handleUpdate}
          setRedirect={this.props.setRedirect}
        />
        <div>
             {this.state.word}
        </div>
        <div>
             {this.state.partOfSpeech}
        </div>
        <div>
             {this.state.versions.map((v, i) => <div key={i}>{v}</div>)}
        </div>
        <div>
             {this.state.pronunciation}
        </div>
        <div>
             {this.state.shortdef}
        </div>
      </div>
  );
  }
}

Solution

  • Your constructor lifecycle method runs only once - during the initialisation of the component. If you are expecting new data from parent component, you can re-render your child by using componentDidUpdate() or getDerivedStateFromProps.

    componentDidUpdate(prevProps) {
      if (prevProps.word || this.props.word) {
       this.setState({
         word
       })
      }
    }
    

    I notice that your child component is not manipulating the props, it is just a display-only container. Why don't you just pass the props and display it directly rather than taking the longest route? Your child component can be a functional component:

    
    const GetWordContainer = (props) => {
      return (
          <div>
            <Search
              handleUpdate={props.handleUpdate}
              setRedirect={props.setRedirect}
            />
            <div>
                 {props.word}
            </div>
            <div>
                 {props.partOfSpeech}
            </div>
            <div>
                 {props.versions.map((v, i) => <div key={i}>{v}</div>)}
            </div>
            <div>
                 {props.pronunciation}
            </div>
            <div>
                 {props.shortdef}
            </div>
          </div>
      );
    }