8

UPDATE: Yes for use case 1, if I extract search.value outside the useEffect and use it as a dependency it works.

But I have an updated Use case below

Use Case 2: I want to pass a searchHits Object to the server. The server in turn return it back to me with an updated value in response. If I try using the searchHits Object I still get the infinite loop

state: {
    visible: true,
    loading: false,
    search: {
        value: “”,
        searchHits: {....}, 
        highlight: false,
    }
}

let val = search.value
let hits = search.searchHits
useEffect( () => {

    axios.post(`/search=${state.search.value}`, {hits: hits}).then( resp => {
        …do something or ..do nothing
        state.setState( prevState => {
            return {
                …prevState,
                search: {... prevState.search, hits: resp.hit}
            }
        })
    })
}, [val, hits])

Use Case 1: I want to search for a string and then highlight when I get results

e.g.

state: {
    visible: true,
    loading: false,
    search: {
        value: “”,
        highlight: false,
    }
}

useEffect( () => {

    axios.get(`/search=${state.search.value}`).then( resp => {
        …do something or ..do nothing
        state.setState( prevState => {
            return {
                …prevState,
                search: {... prevState.search, highlight: true}
            }
        })
    })
}, [state.search])

In useEffect I make the API call using search.value. eslint complains that there is a dependency on state.search , it does not recognize state.search.value. Even if you pass state.search.value it complains about state.search

Now if you pass state.search as dependecy it goes in an infinite loop because after the api call we are updating the highlights flag inside search.

Which will trigger another state update and a recursive loop.

One way to avoid this is to not have nested Objects in state or move the highlights flag outside search, but I am trying to not go that route give the sheer dependecies I have. I would rather have an Object in state called search the way it is. Is there any way to better approach this. If I want to keep my state Object as above how do I handle the infinite loop

Stallion V
  • 83
  • 1
  • 1
  • 3
  • 4
    This is an ongoing issue with linters and their interaction with react-hooks. Nothing in particular is wrong with your code. As is, its safe to ignore this warning. – Chris Ngo Oct 16 '19 at 22:46
  • If it is an esLint issue I am happy to ignore but I really want to be sure. Because the issue comes only when we use Objects inside dependencies – Stallion V Oct 18 '19 at 17:05
  • Avoid using a big object with useState. If you must, do it through useReducer instead. – Samer Buna Oct 20 '19 at 16:02
  • @SamerBuna I have used useReducers and it still goes into a loop. – Stallion V Oct 22 '19 at 21:45

1 Answers1

5

Just a eslint stuff bug may be. You have retracted some code by saying //do something and have hidden he code. Are you sure that it doesn't have anything to do with search object?

Also, try to extract the variable out before useEffect().

const searchValue = state.search.value; useEffect(()=>{// axios call here},[searchValue])

If your search value is an object, react does shallow comparison and it might not give desired result. Re-rendering on a set of object dependencies isn't ideal. Extract the variables.

React does shallow comparison of dependencies specified in useEffect

eg.,

const {searchParam1, searchParam2} = search.value;
useEffect(() => {
//logic goes here
}, [searchParam1, searchParam2]);

Additionally, you can add dev dependency for eslint-plugin-react-hooks, to identify common errors with hooks

Karthik R
  • 5,523
  • 2
  • 18
  • 30
  • do something does no have anything significant and can be removed. Yes Extracting the variable out works but then it only works because search.value is a string. If search.value is a Object it still goes in infinite loop – Stallion V Oct 18 '19 at 16:37
  • @StallionV passing an object as useEffect dependency is bad idea. In general, you don't want re-render based on object, rather set of variables. React would do a shallow comparison of object and not deep comparison. So extract all variables you need from search.value and add them as dependencies. Will update answers – Karthik R Oct 19 '19 at 01:09
  • 2
    I understand that. But then how do you tackle a problem of updating State objects based on an API response. APIs have to be with useEffect, If I want to post the that Object to the server it has to be a dependency. Now in that case how do I update based on the server response and avoid the infinite loop – Stallion V Oct 22 '19 at 22:03
  • Think about this in a practical situation. you are doing it useeffect meaning that you want this data based on some variables on start. If you want this to happen only once, you pass empty [] as dependency else if you want this as the variable changes, add them as dependency. Typically, `post` on useEffect is a rare call and I wonder which such scenarios apply. – Karthik R Oct 23 '19 at 02:47
  • The situation can be you pass some data object from your state to Server, which in turn massages the data and changes it and passes it back to you. You now need to update that data back in the state. – Stallion V Oct 23 '19 at 20:56