简体   繁体   中英

React usecallback not honoring dependency change caused by child function

Hopefully someone can help me with this, I will be quite grateful. I searched though previously answered questions and I can't find something exactly quite like this.

In a nutshell:

I have a useCallback react hook which renders a set of buttons. When one of those buttons are clicked, I need the whole function to re-render to reflect the desired changes. As near as I can tell, I have all the necessary dependencies to achieve this but it's now happening. When I trigger a re-render via other action, the updates are there.

This main callback function is being called inside the base render. Clicking the rendered button does not cause it to re-render. What gives??

  const [pendingArticles, setPendingArticles] = useState([]);
  const [persons, setPersons] = useState([]);

  const renderArticlePieces = useCallback(() => {
    const selectedArticle = pendingArticles.find(article => article.selected);

    function assignArticlePieceToPerson(pieceId) {
      console.log(pendingArticles);
      const selectedArticleIndex = _.findIndex(pendingArticles, { selected: true });

      pendingArticles[selectedArticleIndex].json_content[pieceId].speaker = selectedPerson.id;

      setPendingArticles(pendingArticles);
    }

    function renderPersonAssignmentButtons(pieceId) {
      const currentPiece = pendingArticles.find(article => article.selected).json_content[pieceId];
      if (currentPiece.speaker) {
        return 'Assigned'
      }
      return personsAvailableToAssign.map((person, dex) => {
        return (
          <Button key={dex} onClick={() => assignArticlePieceToPerson(pieceId)}>{`${person.first_name} ${person.last_name}`}</Button>
        )
      }).concat(
        <Button key={personsAvailableToAssign.length + 1}>Other</Button>
      )
    }


    return selectedArticle?.json_content.map((item, dex) => {
      return (
        <div key={dex} style={{ backgroundColor: "#c7c7c7", borderRadius: "5px", padding: "5px", margin: "5px" }}>
          <div>
            Assign to:
            {renderPersonAssignmentButtons(dex)}
          </div>
          <div>{item.content}</div>
        </div>
      )
    })
  }, [pendingArticles, setPendingArticles, personsAvailableToAssign, selectedPerson, ])

The problem might be, that React uses a "shallow" comparison to check if something in the state changes. That means React won't go into your array and check each single array entry if it has changed.

Because you are mutating your existing array, React won't notice the changes. Instead you should create a new array, for example using the spread-syntax:

let new_pending_articles = [...pendingArticles];
new_pending_articles[selectedArticleIndex].json_content[pieceId].speaker = selectedPerson.id;
setPendingArticles(new_pending_articles);

This way, because you are not mutating the array but completely replacing it, React should notice the change and trigger a re-render. Check this post as well: React doesn't rerender on an array state update

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