繁体   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