2

I'm doing something like the following

(https://stackoverflow.com/a/57847874/433570)

 const LowerCard = (props) => {

   let { review_meta, step } = props
   const [component, setComponent] = useState('');
   const {state, editDispatch} = useContext(ReviewMetaEditCardDispatch);

   useEffect(() => {

     // https://stackoverflow.com/a/57847874/433570

     const setDynamicComponent = async () => {
       let DynamicComponent
       switch(state.step) {
       case 'title':
         DynamicComponent = await import('review/react/components/review-meta-edit-card/step/title')
         break
       }

       if (DynamicComponent) {
         setComponent(DynamicComponent.default)
       }
     }

     setDynamicComponent()
   }, []);


   if (!component) {
     return null
   }

   return (
     <View>
       <component/>
     </View>
   )
   }

The above code works, but when I remove [] at the useEffect call, it results in index.js:1 Warning: Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. You can only call Hooks at the top level of your React function.

I'm guessing it's complaining you shouldn't call setComponent (which is a hook) inside useEffect.

How do I overcome this?

eugene
  • 39,839
  • 68
  • 255
  • 489

2 Answers2

2

You should not use async function directly inside useEffect.

here is my solution see if it works



const LowerCard = props => {
  let {review_meta, step} = props;
  const [component, setComponent] = useState('');
  const {state, editDispatch} = useContext(ReviewMetaEditCardDispatch);

  const setDynamicComponent = useCallback(async () => {
    let DynamicComponent;
    switch (state.step) {
      case 'title':
        DynamicComponent = await import('review/react/components/review-meta-edit-card/step/title');
        break;
    }

    if (DynamicComponent) {
      setComponent(DynamicComponent.default);
    }
  }, [state]);

  useEffect(() => {
    // https://stackoverflow.com/a/57847874/433570
    setDynamicComponent();
  }, [setDynamicComponent]);

  if (!component) {
    return null;
  }

  return (
    <View>
      <component />
    </View>
  );
};


Harish Jangra
  • 381
  • 5
  • 16
1

Your dynamic component is using hooks and if you call importedComponent.default React will throw and error error of using hooks inside of useEffect.

Instead, load the component and call .default when you render it.

I've also added state.step to useEffect dependencies array to make sure to re-run the effect if when state.step changes as if on initial render state.step is not title the component will never render as you provided an empty dependencies array

useEffect(() => {
  // same as before
  // ...
  if (DynamicComponent) {
    setComponent(DynamicComponent);
  }
}, [state.step])

if (!component) {
  return null;
}

return (
  <View>
    <component.default />
  </View>
)
Asaf Aviv
  • 11,279
  • 1
  • 28
  • 45
  • DynamicComponent.default is not calling it but just reading a reference. attaching .default inside useEffect or in render makes no difference – Max Mar 19 '20 at 09:39
  • 1
    @Max It actually does make a difference, if you call `DynamicComponent.default` inside `useEffect` React will error as it's returning the result of the default exported component – Asaf Aviv Mar 19 '20 at 09:41
  • @Max https://codesandbox.io/s/peaceful-leaf-wdk6l remove the commented lines and see the difference, make sure to comment the other ones – Asaf Aviv Mar 19 '20 at 09:44
  • it was indeed because of `*.default` – eugene Mar 19 '20 at 09:58
  • @AsafAviv I tried your example and it indeed works with `setComponent(DynamicComponent)`. However it's still not clear to me why this happens, because here `setComponent(DynamicComponent.default)` we are not calling a function - calling it would be `setComponent(DynamicComponent.default())`. I will do some additional investigation when I have more time – Max Mar 19 '20 at 10:09
  • @Max Yes you're right it doesn't execute the function, i wonder why it does this as well – Asaf Aviv Mar 19 '20 at 10:17