Search code examples
reactjsreduxredux-toolkit

why useSelector() with conditions EslintError


Why this would be a violation of the hooks rules.?

In my case:

const getTopicId = useSelector((state) => state.topics.selectTopic.topicId) !== "" ? useSelector((state) => state.topics.selectTopic.topicId) : JSON.parse(localStorage.getItem("topic")).topicId;

I don't put a condition inside the useSelector, i try to say that if useSelector() has data, use it, if not use the localStorage data.

the solution that accept EsLint is: Put the useSelector inside a variable and then inside the ternary....but i don't understant why.

const getTopicSelector = useSelector((state) => state.topics.selectTopic.topicId);

 const getTopicId = useSelector((state) => state.topics.selectTopic.topicId) !== "" ? getTopicSelector : JSON.parse(localStorage.getItem("topic")).topicId

Thanks for your explanations.


Solution

  • Your code sometimes calls useSelector once and sometimes twice.

    If you take

    const getTopicId = useSelector((state) => state.topics.selectTopic.topicId) !== "" ? useSelector((state) => state.topics.selectTopic.topicId) : JSON.parse(localStorage.getItem("topic")).topicId;
    

    and rewrite the ternary to if..else, you can see that clearly:

    let getTopicId;
    const conditionTopicId = useSelector((state) => state.topics.selectTopic.topicId)
    if (conditionTopicId !== "") {
      getTopicId = useSelector((state) => state.topics.selectTopic.topicId)
    } else {
      getTopicId = JSON.parse(localStorage.getItem("topic")).topicId
    };
    

    Sometimes you call a hook once, sometimes twice. This violates the rules of hooks and will make React crash in the long run.

    In your case, you can rewrite that quite simple:

    let getTopicId = useSelector((state) => state.topics.selectTopic.topicId)
    if (getTopicId == "") {
      getTopicId = JSON.parse(localStorage.getItem("topic")).topicId
    }
    

    And suddenly you call the hook only once - and you don't do it conditionally.

    You can also go with a ternary and put the logic inside one hook call:

    const getTopicId = useSelector(
      (state) => 
        state.topics.selectTopic.topicId !== "" ? 
          state.topics.selectTopic.topicId :  
          JSON.parse(localStorage.getItem("topic")).topicId
      )
    

    All of these alternatives are fine - the only important thing: don't call useSelector inside a condition (and a ternary is just a short way of writing if...else).
    React does not allow for that and it will crash on you sooner or later if you do.