首页 > 解决方案 > React Hooks, how to handle dependency If i want to run only when one of them changes?

问题描述

I wanted to ask if there's a proper way of handling a situation when we have an useEffect that we only care it runs when one variable of the dependency array is changed.

We have a table that has pagination and inputs to filter the content with a button.

When the user changes the action (inputs) we update this state and when the user press search we fetch from the api.

When we have results paginated, we then hook on the page and if it changes we then fetch the corresponding page.

I solved the issue by having the ref of action and checking if the previous value was different from the current value. Though I don't know if this is the best practice

I did something like this.

const FunctionView = () => {

 const actionRef = useRef({})

 // action object have query params for the api

 const fetchData = useCallback((page) => {
  // call and api and sets local values
 }, [action])

 // this hook handle page next or previous
 useEffect(() => {
   let didCancel = false
   if (isEqual(Object.values(action), Object.values(actionRef.current))) {
     if (!didCancel) fetchData(page + 1)
   }
   actionRef.current = action
   return () => {
     didCancel = true
   }
 }, [page, fetchData, action])
 return (
  <>
    Components here that changes {action} object, dates and category
   <Button onClick={() => fetchData(page)}>Search</Button>
   <Table>...</Table>
  </>
)

}

I know I can set the dependency array to only page but react lint plugin complains about this so I ended up with warnings.

标签: reactjsreact-hooks

解决方案


A common mistake is trying to directly wire the state of forms up to the data they represent. In your example, you DO want to perform the search when action changes. What you're actually trying to avoid is updating action every time one of the inputs changes prior to submit.

Forms are, by nature, transient. The state of the form is simply the current state of the inputs. Unless you really want things to update on every single keystroke, the storage of these values should be distinct.

import { useCallback, useEffect, useState } from 'react'

const Search = () => {
  const [action, setAction] = useState({})

  const loadData = useCallback(() => {
    // call api with values from `action`
  }, [action])

  useEffect(loadData, [loadData])

  return (
    <>
      <SearchForm setRealData={setAction} />
      <Table>...</Table>
    </>
  )
}

const SearchForm = ({ setRealData }) => {
  // don't want to redo the search on every keystroke, so
  // this just saves the value of the input
  const [firstValue, setFirstValue] = useState('')
  const [secondValue, setSecondValue] = useState('')

  const handleFirstChange = (event) => setFirstValue(event.target.value)
  const handleSecondChange = (event) => setSecondValue(event.target.value)

  // now that we have finished updating the form, update the state
  const handleSubmit = (event) => {
    event.preventDefault()
    setRealData({ first: firstValue, second: secondValue })
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={firstValue} onChange={handleFirstChange} />
      <input value={secondValue} onChange={handleSecondChange} />
      <button type="submit">Search</button>
    </form>
  )
}

推荐阅读