Search code examples
reactjscomponentsreact-hooks

Component only gets updated when wrapped in a `div`


I'm facing an issue where my component only gets updated when wrapped in a div.

I have a switch statement to render different versions of a child. It seems that everything works when I wrap my second case in a div, but if this div isn't present it doesn't work as expected. It seems that the _getCurrentInputState and selectedBlock don't update as they should.

const Parent= () => {

    {/* ... */}

    const _renderEditorPanel = () =>{
        const currentBlockType = blockList[selectedBlock].type
        switch(currentBlockType){
          case 'description': return (
            <EditorPanel 
              currentBlockType={'description'}
              selectedBlock={selectedBlock}
              _getBlock={_getBlock}
              _getCurrentInputState={_getCurrentInputState}
              _setCurrentInputState={_setCurrentInputState}
            />
          )
          case 'skills_and_requirements': return (
            <div>  {/* <<<<<<<<<<<<<<<<<<<<<<<< This is needed to really update the child?
            */}
            <EditorPanel 
              currentBlockType={'skills_and_requirements'}
              selectedBlock={selectedBlock}
              _getBlock={_getBlock}
              _getCurrentInputState={_getCurrentInputState}
              _setCurrentInputState={_setCurrentInputState}
            />
            </div> {/* <<<< and this.*/}
          );
          default: return (
            <EditorPanel 
              currentBlockType={'default'}
              selectedBlock={selectedBlock}
              _getBlock={_getBlock}
              _getCurrentInputState={_getCurrentInputState}
              _setCurrentInputState={_setCurrentInputState}
            />
          );
      }
      }


      return (    
        <StyledJobEditor>
          {_renderEditorPanel()}    
          <div className="preview_panel">
            <div>
            <button onClick={
              () => setSelectedBlock("1")
            }>1</button>
            <button onClick={() => setSelectedBlock("2")}>2</button>
            <button onClick={() => setSelectedBlock("0")}>0</button>
            </div>

              {/*
                The sidebar will be an arraw of block. We should expect to use the .map function here.
              */}

          </div>
        </StyledJobEditor>
      );
    }

Solution

  • I believe the reason you're seeing this is that react has no way to tell that it should mount a new instance of the same component. By changing one to be wrapped in a div react sees that its different and remounts everything. Otherwise, it just treats it as a prop change.

    Without seeing the implementation of EditorPanel I can't give anymore insight, but I'd guess that your unchanged props are only set once on mount or something similar.

    Just like when mapping elements, you can provide a key to each element in the switch to inform react that each one is unique.

    switch (currentBlockType) {
      case 'description': return (
        <EditorPanel
          key="description"                 // Add this
          currentBlockType={'description'}
          selectedBlock={selectedBlock}
          _getBlock={_getBlock}
          _getCurrentInputState={_getCurrentInputState}
          _setCurrentInputState={_setCurrentInputState}
        />
      )
      case 'skills_and_requirements': return (
        <EditorPanel
          key="skills_and_requirements"      // Add this
          currentBlockType={'skills_and_requirements'}
          selectedBlock={selectedBlock}
          _getBlock={_getBlock}
          _getCurrentInputState={_getCurrentInputState}
          _setCurrentInputState={_setCurrentInputState}
        />
      )
      default: return (
        <EditorPanel
          key="default"                      // Add this
          currentBlockType={'default'}
          selectedBlock={selectedBlock}
          _getBlock={_getBlock}
          _getCurrentInputState={_getCurrentInputState}
          _setCurrentInputState={_setCurrentInputState}
        />
      )