簡體   English   中英

父組件在父狀態更改時不必要地重新渲染子級

[英]Parent component unnecessarily re-rendering child on parent state change

我正在創建一個簡單的Magic The Gathering搜索引擎。 願景是列出搜索結果,單擊搜索結果后,主顯示屏將顯示有關所選卡的擴展信息。

在這里你可以看到它

頂級App組件包含要顯示什么卡的狀態,並且ScrollView組件僅在列表中突出顯示所選卡的情況下,保持所選卡的狀態。 我向下傳播setDisplayCard處理程序,以便在列表中單擊某個卡時,可以將顯示卡設置為回調。

function App(props) {

  const [displayCard, setDisplayCard] = useState(null)

  return (
    <div className="App">
      <SearchDisplay handleCardSelect={setDisplayCard}/>
      <CardDisplay card={displayCard} />
    </div>
  );
}
function SearchDisplay({handleCardSelect}) {

    const [cards, setCards] = useState([]);

    useEffect(() => {
        (async () => {
            const cards = await testCardSearch();
            setCards(cards);
        })();
    }, []);

    async function handleSearch(searchTerm) {
        const searchCards = await cardSearch({name: searchTerm});
        setCards(searchCards)
    };

    return (
        <StyledDiv>
            <SearchBar 
                handleSubmit={handleSearch}
            />
            <ScrollView 
                handleCardSelect={handleCardSelect} 
                cards={cards}
            />
        </StyledDiv>
    );
}
function ScrollView({cards, handleCardSelect}) {

    const [selected, setSelected] = useState(null);

    return (
        <ViewContainer>
            {cards.map((card, idx) =>
                <li 
                    key={idx} 
                    style={selected === idx ? {backgroundColor: "red"} : {backgroundColor: "blue"}}
                    onClick={() => {
                        setSelected(idx);
                        handleCardSelect(card);
                    }}
                >
                    <Card card={card} />
                </li>
            )}
        </ViewContainer>
    );
}

我遇到的問題是調用setDisplayCard重新渲染了ScrollView並消除了所選卡的本地狀態,因此我無法在列表中突出顯示活動卡。 根據我對react的理解,我不明白為什么ScrollView重新渲染,因為它不依賴於displayCard的狀態。 我不確定采取什么方法來解決它。 當我單擊列表中的卡時,我希望它突出顯示紅色。

默認情況下(無狀態)組件會在3種情況下重新渲染

  1. 道具變了
  2. 狀態已經改變
  3. 這是父母的回報

可以使用shouldComponentUpdate組件或memo為無狀態組件來更改此行為。

// If this function returns true, the component won't rerender
areEqual((prevProps, nextProps) => prevProps.cards === nextProps.card)

export default React.memo(ScrollView, areEqual);

但是我不認為這是您的問題。 您正在使用數組索引idx作為元素鍵,這通常會導致意外行為。

嘗試刪除key={idx}並檢查是否可以解決您的問題。

一旦調用父組件的render方法,子組件的render方法將始終被調用。 如果道具或狀態發生變化,也是如此。

由於您正在使用功能組件,因此可以使用React.memo HOC來防止不必要的組件重新渲染。

React.memo行為類似於PureComponent並且將ScrollView的舊props與新props進行PureComponent比較,並且僅當它們不相等時才觸發重新渲染:

export default React.memo(ScrollView);

React.memo還有第二個參數,它使您可以控制比較:

function areEqual(prevProps, nextProps) {
  // only update if a card was added or removed
  return prevProps.cards.length === nextProps.cards.length;
}

export default React.memo(ScrollView, areEqual);

如果要使用基於類的組件,則也可以使用shouldComponentUpdate生命周期方法。

因此,您的App組件應該保存用戶單擊的卡的狀態? 現在,您的App組件是無狀態的。 這是一個功能組件。 嘗試將其轉換為具有初始狀態和維護狀態的類組件。

setDisplayCard()的邏輯是什么?

我在React 16中聽說過嗎? 像“ useState()”和“掛鈎”之類的東西,但我並不熟悉。

這個人似乎有類似的問題,

使用狀態來反應功能組件

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM