最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to implement conditional rendering while the children component calling useState() in react hooks? - Stack Over

programmeradmin2浏览0评论

Recently I am trying to replace class ponents implementation in my project with React Hooks and I have some troubles in implementing conditional rendering of children ponent.

I have a parent ponent contains header, footer and a conditional rendering children ponent which is rendering different children ponent depends on the state of parent ponent and its state is controlled by another useEffect like the coded stated below.

However, one of my children ponent contains a simple counter which is implemented by useState() like the example in official React Hooks tutorial. 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 ponent or use Redux-like implementation? But it is a bit awkward because the counter is just a simple logic and not necessary to be put out of the ponent.

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 ponent:

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:

   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
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Recently I am trying to replace class ponents implementation in my project with React Hooks and I have some troubles in implementing conditional rendering of children ponent.

I have a parent ponent contains header, footer and a conditional rendering children ponent which is rendering different children ponent depends on the state of parent ponent and its state is controlled by another useEffect like the coded stated below.

However, one of my children ponent contains a simple counter which is implemented by useState() like the example in official React Hooks tutorial. 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 ponent or use Redux-like implementation? But it is a bit awkward because the counter is just a simple logic and not necessary to be put out of the ponent.

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 ponent:

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:

   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
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Share Improve this question asked May 17, 2019 at 4:23 Horace TangHorace Tang 532 silver badges4 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

It's perfectly fine to render child ponents 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 piles into). Directly calling child ponents 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>
);
发布评论

评论列表(0)

  1. 暂无评论