简体   繁体   English

当子组件在react钩子中调用useState()时,如何实现条件渲染?

[英]How to implement conditional rendering while the children component calling useState() in react hooks?

Recently I am trying to replace class components implementation in my project with React Hooks and I have some troubles in implementing conditional rendering of children component. 最近,我试图用React Hooks替换项目中的类组件实现,并且在实现有条件的子组件渲染时遇到一些麻烦。

I have a parent component contains header, footer and a conditional rendering children component which is rendering different children component depends on the state of parent component and its state is controlled by another useEffect like the coded stated below. 我有一个父组件,该组件包含页眉,页脚和条件渲染子组件,该组件将根据父组件的状态来渲染不同的子组件,并且其状态由另一个useEffect进行控制,如下所述。

However, one of my children component contains a simple counter which is implemented by useState() like the example in official React Hooks tutorial. 但是,我的一个子组件包含一个简单的计数器,该计数器由useState()实现,就像官方React Hooks教程中的示例一样。 As rules of hooks stated that we can only call hook at the top level, my app is crashed while this children is rendered. 正如钩子规则所述,我们只能在顶层调用钩子,因此渲染此子级时我的应用程序崩溃了。

I guess one of the solution is to put the children's useState() to parent component or use Redux-like implementation? 我猜解决方案之一是将孩子的useState()放到父组件或使用类似Redux的实现? But it is a bit awkward because the counter is just a simple logic and not necessary to be put out of the component. 但这有点尴尬,因为计数器只是一个简单的逻辑,不必从组件中取出。

So I am finding another way to solve this problem. 因此,我正在寻找解决该问题的另一种方法。 Of course please let me know if my concept is wrong at the beginning. 当然,请先让我知道我的概念是否错误。

My parent component: 我的父组件:

const StorePage = (props) => {
    const { children } = props;
    const [detectedTagIds, setDetectedTagIds] = useState([]);
    const [detectedProducts, setDetectedProducts] = useState([]);

    const fetchProductByTagIds = (tagIds) => productController.getProducts({ tagId: tagIds })
        .then(res => res.json())
        .then(json => setDetectedProducts(json.result))

    // monitor detected tags
    useEffect(() => {
        ws.addEventListener('message', (event) => {
            const json = JSON.parse(event.data)
            const { tagId } = json;

            if (!_.includes(detectedTagIds, tagId)) {
                setDetectedTagIds(_.concat(detectedTagIds, tagId));
            }
        });
    }, []);

    // fetch while detected tags are changed
    useDeepCompareEffect(() => {
        fetchProductByTagIds(detectedTagIds)
    }, [detectedTagIds]);

    return (
        <div className="StorePage">
            {Header({ detectedProducts })}
            <div className="StorePage-content">
                {
                    detectedTagIds.length === 0 ?
                    LandingPage() :
                    ( detectedProducts.length === 1 ? ProductInfoPage({ detectedProduct: detectedProducts[0] }) : null )
                }
            </div>
            {Footer({ detectedProducts })}
        </div>
    );
};

export default StorePage;

Here is the error message I have got, I think this is triggered by the change of detectedProducts : 这是我收到的错误消息,我认为这是由detectedProducts的更改触发的:

   Previous render            Next render
   ------------------------------------------------------
1. useState                   useState
2. useState                   useState
3. useEffect                  useEffect
4. useRef                     useRef
5. useEffect                  useEffect
6. useState                   useState
7. useState                   useState
8. useState                   useState
9. useRef                     useState
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It's perfectly fine to render child components conditionally even if those children make use of hooks, but you'll need to do so using the normal react way: either by writing jsx tags, or by manually calling React.createElement (which is what jsx compiles into). 即使这些子组件使用了钩子,也可以有条件地呈现子组件,这是完全可以的,但是您需要使用常规的react方法:通过编写jsx标签或手动调用React.createElement(这是jsx编译的)进入)。 Directly calling child components as functions will cause the problems you are seeing. 直接将子组件作为函数调用将导致您看到的问题。

return (
  <div className="StorePage">
    <Header detectedProducts={detectedProducts} />
    <div className="StorePage-content">
      {detectedTagIds.length === 0 ? (
        <LandingPage/>
      ) : detectedProducts.length == 1 ? (
        <ProductInfoPage detectedProducts={detectedProducts[0]} />
      ) : (
        null
      )}
    </div>
    <Footer detectedProducts={detectedProducts}/>
  </div>
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM