React useEffect在其依赖项数组中具有一个split元素

时间:2019-11-29 15:47:43

标签: reactjs react-hooks

我正在尝试构建一个自定义的钩子,以处理异步调用的加载和错误行为。

我想这样使用它:

const { loading, error} = useFetch(fetchUser, id)

const {loading, error } = useFetch(updateUserName, id, name)

到目前为止,我有类似的东西:

function useFetch(fetchFn, ...params) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);

  useEffect(() => {
    fetchFn(...params)
      .then(() => {
        setLoading(false);
      })
      .catch(() => {
        setError(true);
      });
  }, [...params, fetchFn]);

  return { loading, error };
}

我在useFetch(fetchFn, ...params)上遇到了麻烦,因为在每个调用中都会创建一个新的params数组。所以我不能在依赖项数组中使用它。但是我不能使用传播运算符。

是否有可能实现我想做的事情?

3 个答案:

答案 0 :(得分:0)

大概您希望单个参数是依赖项,而不是这些参数的数组?因为在这种情况下,依赖关系实际上只是数组实例(正如您所说的,每次都会更改)。

不要在依赖项列表中散布参数,而是包括这些参数在依赖项列表中,即

useEffect(() => {
  fetchFn(...params)
    .then(() => {
      setLoading(false);
    })
    .catch(() => {
      setError(true);
    });
}, [fetchFn].concat(params));

答案 1 :(得分:0)

我设法表现出想要的行为:

function useFetch(fetchFn, ...params) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);

  useEffect(() => {
    fetchFn(...params)
      .then(() => {
        setLoading(false);
      })
      .catch(() => {
        setError(true);
      });
  }, params);

  return { loading, error };
}

我只是将params作为依赖项数组传递。当然,eslint并不满意,但是我们可以直接禁用它。 我相信此实现是正确

请随时分享您的观点,我对您的反馈意见非常感兴趣

答案 2 :(得分:-1)

这样做怎么样?

注意:这不是可运行的代码。它甚至没有被转译。它缺少部分代码,应该有很多错别字。仅供参考。

/*
Firstly, let's define a function to determine if two arrays have the same elements.
*/
const hasSameValues = (arr1 = [], arr2 = []) => {
  if (arr1?.length !== arr2?.length) {
    return false;
  }

  for (let i = 0, length = arr1?.length; i < length; i++) {
    if (arr1[i] !== arr2[i]) {
      return false;
    }
  }
  return true;
}

/* 
 Now we define the compoenent that will receive the dependencies array
*/

const AircraftComboBox = ({source = () => {}, dependencies = [], onChange = () => {}}) => {
  
  // Let's save the dependencies to evaluate if they've changed in the next render
  const [storedDependencies, setStoredDependencies] = useState(dependencies);

  const [availableAircrafts, setAvailableAircrafts] = useState([]);

  useEffect(
    () => {
      // Use the hasSameValues function to determine in runtime if the array of dependecies has changed
      setStoredDependencies(prev => hasSameValues(prev, dependencies) ? prev : dependencies)
    },
    [dependencies, setStoredDependencies] // this array of dependencies still hardcoded
  );

  useEffect(() => {
    async function fetchData() {
      const response = await source(); // Call the source function passed in props
      setAvailableAircrafts([...response.data]);
    }
    
    fetchData();
  }, [storedDependencies]); // this array of dependencies still hardcoded


  return (
    <>
      <label for="aircrafts-combo">Aircraft</label>
      <select id="aircrafts-combo" onChange={e => onChange(e)}>
        { availableAircrafts.map(option => <option value={option}>{option}</option>) }
      </select>
    </>  
  );
}



/**
  Now we can use it.
  Note that the Component using AircraftComboBox doesn't need to implement any useEffect.
 */
 const AircraftFeature = () => {
   const [category, setCategory] = useState('');
   const [airCraft, setAircraft] = useState('');

   return (
     <>
      <div>
        <label for="category">Category</label>
        <select id="category" type="text" onChange={e => setCategory(e.target.value)} >
          <option value="ac">airplane</option>
          <option value="rc">rotorcraft</option>
          <option value="pl">powered lift</option>
          <option value="gl">glider</option>
          <option value="lta">lighter than air</option>
          <option value="pp">powered parachute</option>
          <option value="wsc">weight-shift-control</option>
        </select>  
      </div>

    {/**
     Every time category's change, source function, defined in props, will be fired.
     This will retrieve new data to populate the list of available options.
     */}

      <AircraftComboBox 
        source={() => httpService.getAirplanesByCategory(category)}
        dependencies={[category]}
        onChange={e => setAircraft(e.target.value)}
      /> 

    </>
   );
 }