0

I have tried finding the answer to this on StackOverflow and there are some related posts (e.g. React Child Component Not Updating After Parent State Change) but I want to understand why this is not working...

I have a React application that will display a layout of character cards (that is, each card displays a different character). It uses a child component, CharacterBoard, that lays out the CharacterCards, which would be a grandchild component. I pass the characters down from the App to the CharacterBoard as props, and CharacterBoard in turn maps these out the CharacterCards.

The problem is that I want the state of the character to change when I click on one of them. Specifically, I want the revealed field to change. However, even though the state change is reflected in the array of characters in the App (that is, the revealed field changes correctly), and the change is reflected in the array of characters in CharacterBoard, but not in CharacterCard. In fact, my mapping does not seem to be called at all in CharacterBoard when the props change.

Do I need to use something like getDerivedStateFromProps in CharacterBoard and set the state of that component and then use the state to map the values down to CharacterCard? If so, why?

In short (tl;dr), can you pass props on down through the component chain and map them out along the way and still have all changes reflected automatically?

Thanks for any guidance.

If it helps, the render method of my App is

render() {

    const {state: {characters}} = this
    return (
      <div>
        <header>
        </header>
        <main>
          <CharacterBoard
            onCardSelected={this.onCardSelected}
            rowSize={logic.ROW_SIZE}
            characters={characters}
            cardSize={this.CARD_SIZE}/>
        </main>
      </div>
    );
  }

that of CharacterBoard is

 render() {
    const {props: {characters, rowSize, cardSize,onCardSelected}} = this
    const rowUnit = 12 / rowSize

    const cardLayout = characters
      .map((character, i) => (
          <Col xs={6} sm={rowUnit} key={character.name}>
            <CharacterCard
              onCardSelected = {onCardSelected}
              key={i + Math.random()}
              character={character}
              cardSize={cardSize}
            />
          </Col>
        )
      )

    return (
      <div>
        <Container>
          <Row>
            {cardLayout}
          </Row>
        </Container>
      </div>
    )
  }

and finally CharacterCard has this render method

 render() {
    const {props: {character, cardSize}} = this
    const {thumbnail, revealed} = character
    const imgURL = `${thumbnail.path}/${cardSize}.${thumbnail.extension}`

    const topCardClass = classNames('characterCard__card-back', {'characterCard__card-back--hidden': revealed})
console.log(revealed)
    return < a href="/#" onClick={this.onCardSelected}>
      <div className='characterCard__card'>
        <div className={topCardClass}>
          <img src="/images/card_back.png" alt=""/>
        </div>
        < div className='characterCard__card-front'>< img alt=''
                                                          src={imgURL}/>
        </div>
      </div>
    </a>

  }
Cerulean
  • 5,543
  • 9
  • 59
  • 111
  • 1
    Passing props like that is quite normal and should work. Do you have any instances of `shouldComponentUpdate`? Those might cause it to not render. I do see one issue where you use a random key for the character card. You shouldn't do that, as it will force extra rerenders, but i don't think it's causing the problem you have. – Nicholas Tower Oct 09 '18 at 16:52
  • @NicholasTower. No, no `shouldComponentUpdate`. I'll fix the random key issue and go back to the code do see if I can figure it out if it should be working as is. – Cerulean Oct 09 '18 at 17:04

1 Answers1

0

Doh! A simple forgetting to setState in App. Knowing that it should work made me go back through the code one more time and see that, indeed, it was a stupid error on my part.

Cerulean
  • 5,543
  • 9
  • 59
  • 111