0

I'm trying to convert a Class-based component to a Functional component. I get the above-mentioned error if I use the same code that was under componentDidMount in useEffect hook.

// Class based component

    class Container extends Component {
      state = {
        elements: [],
        counter: 1,
        bgColor: "#ffffff",
        botTextColor: "#000000",
        botBGColor: "#aaaaaa",
        userTextColor: "#000000",
        userBgColor: "#aaaaaa",
      };
      
      componentDidMount = async () => {
        this.setState({
          bgColor: this.props.chat.currentChat.bgColor,
          botTextColor: this.props.chat.currentChat.botTextColor,
          botBGColor: this.props.chat.currentChat.botBGColor,
          userTextColor: this.props.chat.currentChat.userTextColor,
          userBgColor: this.props.chat.currentChat.userBgColor,
        });
        
        
        this.setState({
          elements:
            this.props.chat.currentChat.elements &&
            this.props.chat.currentChat.elements.length > 0
              ? elements
              : [
                  {
                    id: "0",
                    data: {
                      label: (
                        <WelcomeNode
                          id={"0"}
                          images={this.props.chat.media.map((e) => e.file)}
                          updateChoices={(choices) =>
                            this.updateChoices("0", choices)
                          }
                          updateMessage={(message) =>
                            this.updateMessage("0", message)
                          }
                          updateImage={(e) => this.updateImage(e, "0")}
                          addEdge={this.addEdgeCustom}
                          deleteEdgeChoice={(index) =>
                            this.deleteEdgeChoice("0", index)
                          }
                          isChoiceConnected={(index) =>
                            this.isChoiceConnected("0", index)
                          }
                        ></WelcomeNode>
                      ),
                      message: "",
                      choices: [],
                      type: "welcome",
                      id: "0",
                    },
                    className: "node-elements",
                    position: { x: 100, y: 100 },
                  },
                ],
          counter: elements.length > 0 ? elements.length : 1,
        });
        
       }
      
     }

The Following is the functional component where the error occurs

// Functional component

const initialState = {.....}

const Container = () => {
  const [state, setState] = useState(initialState);
  const { auth, chat } = useSelector((state) => ({ ...state }));
  const dispatch = useDispatch();
  const history = useHistory();

  useEffect(() => {
    setState({
      ...state,
      bgColor: chat.currentChat.bgColor,
      botTextColor: chat.currentChat.botTextColor,
      botBGColor: chat.currentChat.botBGColor,
      userTextColor: chat.currentChat.userTextColor,
      userBgColor: chat.currentChat.userBgColor,
    });

    setState({
        ...state,
        elements:
          chat.currentChat.elements && chat.currentChat.elements.length > 0
            ? elements
            : [
                {
                  id: "0",
                  data: {
                    label: (
                      <WelcomeNode
                        id={"0"}
                        images={chat.media.map((e) => e.file)}
                        updateChoices={(choices) => updateChoices("0", choices)}
                        updateMessage={(message) => updateMessage("0", message)}
                        updateImage={(e) => updateImage(e, "0")}
                        addEdge={(e) => addEdgeCustom(e)}
                        deleteEdgeChoice={(index) =>
                          deleteEdgeChoice("0", index)
                        }
                        isChoiceConnected={(index) =>
                          isChoiceConnected("0", index)
                        }
                      ></WelcomeNode>
                    ),
                    message: "",
                    choices: [],
                    type: "welcome",
                    id: "0",
                  },
                  className: "node-elements",
                  position: { x: 100, y: 100 },
                },
              ],
        counter: elements.length > 0 ? elements.length : 1,
      });

   
  }, []);

}
  

The following error is thrown and the browser crashes Uncaught (in promise) Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

dpacman
  • 3,683
  • 2
  • 20
  • 35
  • useEffect triggers whenever the component renders. setState triggers a re render, by calling setState inside useEffect, you are creating an infinite loop. – Charlie Bamford Feb 09 '21 at 19:38
  • Thanks for the reply, so where can I setState instead? – dpacman Feb 09 '21 at 19:40
  • tbh, I don't understand why your state is keeping track of any of the elements in it. It looks like the values are all available through useSelector and you are just copying them into state. The only thing you're showing is that you are showing is that you are setting a welcome message if the chat is empty, but that could be done as (or more) easily in the regular render process. – Charlie Bamford Feb 09 '21 at 20:14
  • I just read up on useSelector, and now I'm more confused about what you're doing. It looks like you're creating a react redux selector from your state, then you're trying to set state from the selector. Is this correct? – Charlie Bamford Feb 09 '21 at 20:21
  • There are another 10 states that I'm tracking which I haven't included here as It would become too long, I'm accessing the ones available in the redux store using the useSelector – dpacman Feb 09 '21 at 20:24

2 Answers2

0

sorry but your code is too complicated to read please reorganize it to make it more readable and understandable. please try charging useSelector line to this line:

 const { auth, chat } = useSelector((state) => state);

this is causing multi render because useSelector detect state is recreating(using spread operator) so it would rerender the component.

plus in useEffect when you are setting the state use setState callback, this will not override your previous state update :

setState(prev=>({...prev,newState}))
Dharman
  • 30,962
  • 25
  • 85
  • 135
adel
  • 3,436
  • 1
  • 7
  • 20
-2

useEffect usually requires a dependency array. What you use inside of the useEffect hook should go into that array for example we have a function that sets the id. The useEffect dependency will want the id in the array. Thus only update/run this useEffect hook if the id changes.

useEffect(() => {
   setId(id)
}, [id])

If you only want to run the useEffect once on first render you can leave the array blank like this:

useEffect(()=>{
  //http fetch request or something
}, [])
Colin Hale
  • 824
  • 5
  • 11
  • The error still occurs even after including the dependency array, For useEffect to work as componentDidMount, the dependency array must be empty – dpacman Feb 09 '21 at 19:36
  • My guess then is the way you are spreading the props to set the state is causing the rerenders. I Think adding a conditional statement to only run it once inside the useEffect then pass that condition onto the useEffect array to stop future reruns might be a quick hack to get your code working. useEffect(()=>if(isFirst){setState();setIsFirst(false)},[isFirst]) – Colin Hale Feb 09 '21 at 19:42
  • Sure, let me try that – dpacman Feb 09 '21 at 19:46
  • you did set an empty array as the dependency array? Just double checking, like in the second previous example. – Colin Hale Feb 09 '21 at 19:49
  • Yup, it is empty. But I did try with the dependents too, still failed – dpacman Feb 09 '21 at 19:51
  • There is an example here on how to set state using the useEffect. https://stackoverflow.com/questions/53715465/can-i-set-state-inside-a-useeffect-hook – Colin Hale Feb 09 '21 at 19:57
  • Hey Colin, I really appreciate you helping. Will let you know if it works! – dpacman Feb 09 '21 at 20:19
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/228509/discussion-between-dpac-and-colin-hale). – dpacman Feb 10 '21 at 06:39