3

The example below shows how my use of useEffect() causes multiple Ajax requests. On the initial rendering of the component, this request is made twice. If the tag is changed, this request is made twice.

I can use some kind of internal state variables to track whether I have already fetched. Or I can create a kind of stack where I push on potential requests of a certain type, then before returning pop the stack and run the final request. But all of these seem like a hack.

(If it helps, the application is quite a bit larger, and I am using redux. Is a broader solution something like redux-saga's cancellation?)

const Thing => () => {
  const [tagId, setTagId] = useState(null)
  const [query, setQuery] = useState('')

  useEffect(() => {
    doSomeAjaxAndUpdateResults()
    setQuery('')
  }, [tagId])

  useEffect(() => {
    doSomeAjaxAndUpdateResults()
  }, [query])

  // ... bunch of extra effects with similar dependencies.
}
0

2 Answers 2

1

You can just combine both internal state variables into one useEffect hook and then if one change you can do doSomeAjaxAndUpdateResults

const Thing = (prop) => {
  const [tagId, setTagId] = useState(null)
  const [query, setQuery] = useState('')

  useEffect(() => {
    doSomeAjaxAndUpdateResults()
    setQuery('')
  }, [tagId, query])

  return ...;
}

Here's the live example that also relies on props being passed over: https://stackblitz.com/edit/react-wzmxmj

First render is with initial value, second render is with props being passed over and then subsequent props change will trigger one call each.

Sign up to request clarification or add additional context in comments.

2 Comments

This works for the example I provided, but you did so by collapsing the logic into one useEffect method. I have many useEffect() -- I guess I should provide a larger code example!
My answer is within the context of your provided example, update it to your needs and will try again with different solution if it warrnts
0

You are calling the same function in 2 useEffects, what you can do is to check first the useEffects you're watching at if it's '' or null the [tagId] and [query], you can try this code below. Hope it helps

const Thing => () => {
  const [tagId, setTagId] = useState(null)
  const [query, setQuery] = useState('')

  useEffect(() => {
    if(tagId && tagId !== null) {
       doSomeAjaxAndUpdateResults()
       setQuery('')
    }
  }, [tagId])

  useEffect(() => {
    if(query && query !== '') {
      doSomeAjaxAndUpdateResults()
    }
  }, [query])
}

8 Comments

Javascript considers empty strings falser, so query !== '' will only be false when query is also false. Also, in the case of checking against null or undefined, == or != will match both null and undefined, which is often what you want (but usually === or !== are preferred in other cases).
But in useEffect, it will initially run the useEffect [query] and [tagId] even if it's '' or null so you have to check it first before doing actions when [query] and [tagId] values changed, that's why it keeps rerendering.
I think then your logic is backwards... maybe you wanted if (!tagId && tagId !==null)… and if(!query && query !== '')… instead? This makes it so it only runs tagID related logic if the value is falsey but not exactly null, and runs query related logs only if the query is falsey but not exactly ''
Nope, if(tagId) means you're checking if it has value, lmao
Michael, It works fine, and it's a pretty decent habit. The one drawback is that it makes me wonder if you really meant to do something else, so it's unclear in that regard, because I overthink it. Javascript truthiness has bit me so many times....
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.