简体   繁体   中英

Child component props not reflecting state change from parent

The part of my app in question is utilizing 4 components, 1 parent and 3 children. As expected, the parent component handles all state and passes values to child components via props.

The parent component contains methods for updating state changes, also passed down via props. The child components do not have state other than component methods, they use only props for visual data.

The problem I'm having is that when I call the method to update parent state, the parent state is updated successfully (verified via the console), however the child component, which reflects this state via its props, doesn't render the state change visually.

The code is below which I'll do my best to explain:

Parent component update state method:

handleUpvoteState(blurtIndex) {
    let blurts = [...this.state.followingBlurts],
        updatedBlurt = {...blurts[blurtIndex]};
    updatedBlurt.vote++;
    blurts[blurtIndex] = updatedBlurt;
    this.setState({
        followingBlurts: blurts
    });
    console.log(this.state.followingBlurts[blurtIndex].vote); // State change reflected.
}

Parent component passing state to child props:

<LazyBlurtsPanel
     appendDate={this.state.appendDate}
     random={this.state.randomize} 
     blurts={this.state.followingBlurts} 
     handleLazyState={this.handleLazyState}
     handleUpvoteState={this.handleUpvoteState}
     lazyElement='lzyfollowing'
     apiMethod={this.state.apiMethod}
     currentUser={false}
 />

The lazy panel component (above) then sends data to footer child via props:

<BlurtFooter 
   voteCount={this.props.blurts[i].vote}
   currentUser={this.props.currentUser}
   blurtId={this.props.blurts[i]._id}
   blurtIndex={i}
   handleUpvoteState={this.props.handleUpvoteState}
 />

When I call

this.props.handleUpvoteState(index)

in my child footer component, the parent state is successfully updated. However, the footer component doesn't re-render to reflect the parent's updated state.

Here's the footer component code that isn't getting re-rendered:

render() {
    return (
        <div className="blurt-panel-footer">
           <UpvoteCount voteCount={this.props.voteCount}/>

Any help much appreciated.

EDIT: There was some confusion as to how blurtfooter is called, so I'm including the code for how the panel is built inside a loop within LazyBlurtPanel component. Here's the code for that:

for(let i=numRendered; i<renderCount; i++) {
  if (this.props.blurts[i]) {
    renderBlurts.push(
         <div className='blurt-panel' id={(((i + 1) % 6) === 0) ? this.props.lazyElement : 'false'} key={i}>
              <BlurtHeader blurt={this.props.blurts[i]} />
              <div className='blurt-panel-body'>
                  <Sanitizer dirty={ this.props.blurts[i].text } />
                  {
                      (this.props.blurts[i].blurtImg !== 'false')? <img src={'/images/blurt/' + this.props.blurts[i].blurtImg} /> : ''
                  }
              </div>
              <BlurtFooter 
                  voteCount={this.props.blurts[i].vote}
                  currentUser={this.props.currentUser}
                  blurtId={this.props.blurts[i]._id}
                  blurtIndex={i}
                  handleUpvoteState={this.props.handleUpvoteState}
                  key={this.props.blurts[i]._id} //Tried with and without key here..
              />
          </div>
      );
  }
}

Try changing how BlurtFooter is called. Even if this doesn't fix it it will still be more maintainable code

render() {
  const { currentUser, blurts, lazyElement, handleUpvoteState } = this.props;
  return (
    <Fragment>
      {
        blurts.filter(x => x).map((blurt, index) => (
          <div className="blurt-panel" id={(((index + 1) % 6) === 0) ? lazyElement : 'false'} key={blurt._id}>
              <BlurtHeader blurt={blurt} />
              <div className="blurt-panel-body">
                  <Sanitizer dirty={blurt.text} />
                  {
                      blurt.blurtImg !== 'false' &&
                      <img src={`/images/blurt/${blurt.blurtImg}`} />
                  }
              </div>
              <BlurtFooter 
                  voteCount={blurt.vote}
                  currentUser={currentUser}
                  blurtId={blurt._id}
                  blurtIndex={index}
                  handleUpvoteState={handleUpvoteState}
              />
          </div>
        )
      }
    </Fragment>
  );
}

There are a lot of other improvements that can be made, but see how that works, especially setting key correctly is a must.

I'm not sure what that id is on the div but that looks fishy as well and shouldn't be there.

Also the part blurts.filter(x => x) shouldn't be needed, so state should be improved to not need that

Updated : Make sure to take latest code above

State should always update in a nonmutating way. This way react core library could understand there is a change in data. Array and objects use reference pointers in memory. So even if we update a value inside those reffrence type variables it won't affect the original memory location of those variables. So create a new variable in memory and copy the state that you need to update and then set the state by using the new variable. This way we can avoid this issue. Also you can create a nonmutating structure using modern es6 + methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM