如何在我的情况下仅使用一次React hook useEffect?

时间:2019-10-26 05:32:57

标签: javascript reactjs redux react-redux react-hooks

我认为我的问题似乎重复了一段时间,但我想不是,我已经检查了许多标题相同但大小写不同的问题

问题是我被迫在代码中两次使用react钩子,甚至想将它们变成三个,但是我觉得这是一个不好的做法,并且我肯定有解决方法或解决方案

我拥有最初要获取的数据,并且通过首次useEffect调用实现了该数据,以后可以对数据进行过滤或排序,并且我具有过滤器复选框和一个用于排序查询的选择,复选框将过滤数据并选择一个选项将其排序,我正在使用redux和react-redux connect 方法创建状态树,其中包括过滤器 sortBy < / strong>

  //the first call
  useEffect(() => {
    loadProducts();
  }, []);


//the second call
  useEffect(() => {
    loadProducts(filters, sortBy)
  }, [filters, sortBy])

const mapStateToProps = (state) => ({
  filters: state.filters,
  sortBy: state.sortBy
});


const mapDispatchToProps = (dispatch) => ({
  loadProducts: (filters, sortBy) => {
    fetch('certain API').then(res => res.json()).then(json => {
      dispatch(fetchProducts(json.products, filters, sortBy));
    }).catch(err => 'error fetching data');
  }
});

现在,第一个调用是最初从API检索数据,loadProducts函数不需要任何参数,第二个调用是在进行过滤或排序时,在这种情况下,该函数需要参数,并且在有任何过滤器时都可以使用或sortBy被更改

我什至考虑通过将第二个呼叫分成两个呼叫来使它们成为三个呼叫:

 useEffect(() => {
    loadProducts(filters)
  }, [filters])

useEffect(() => {
    loadProducts(undefined, sortBy)
  }, [sortBy])

那是因为我不想每次都仅进行过滤和排序,即使只有其中一个可以工作,也因为我认为当用户只适合数据时排序也可以进行,反之亦然。

这是我的 fetchProducts 操作:

import { filterProducts, sortProducts } from './../../util/util';
export const fetchProducts = (products, filters, sortBy) => {
    if (filters && filters.length) {
        products = filterProducts(products, filters);
    }

    if (sortBy) {
        sortProducts(products, sortBy);
    }

    return {
        type: FETCH_PRODUCTS,
        payload: products
    }
}

2 个答案:

答案 0 :(得分:3)

在fetchProducts中,您是在对产品进行突变,请改用products = sortProducts([...products], sortBy);

如果state.filters的初始值为[],sortBy的初始值为false或有效的默认排序,那么您只需要一个效果。

正如skyboyer所说;如果您在用户更改过滤器或排序时执行异步处理,则应避免使用race condition

您可以通过以下方式编写效果:

useEffect(() => {
  const cancel = {current:false};
  //add cancel, only dispatch fetchProducts when it's
  //  the latest result from user interaction
  loadProducts(filters, sortBy, cancel);
  return ()=>cancel.current=true;
  //added loadProducts as an effect dependency
  //  the linter should have warned you about it
}, [filters, loadProducts, sortBy])

const mapDispatchToProps = (dispatch) => ({
  loadProducts: (filters, sortBy, cancel) => {
    fetch('certain API').then(res => res.json()).then(json => {
      //only dispatch if this is the latest list of products
      //  if user changed something during fetching then don't
      //  set products
      cancel.current || dispatch(fetchProducts(json.products, filters, sortBy));
    }).catch(err => 'error fetching data');
  }
});

如果过滤器最初为[],并且sortBy最初具有工作值或为false,则应该可以工作。

答案 1 :(得分:2)

如果要在一个useEffect调用中处理所有逻辑,则可以从useEffect中取出所有prop依赖项[filters, sortBy]

useEffect(() => {
  //This runs all the time a component (re)renders
});

现在在useEffect内部,您必须根据道具更改有条件地调用提取动作。您可以使用useRef来跟踪道具的先前值。下面是草稿。

function App(props){
  const {products = [], filter, sortBy} = props;
  const filterRef = useRef();
  const sortRef = useRef();
  const loadedRef = useRef(false); 


  // The effect runs all the time
  useEffect(() => {
    if(!loadedRef.current){
      console.log("Dispatch the loadProducts action");
      //Update ref
      loadedRef.current =  true;
    }else{
      if(sortBy !== sortRef.current){
        console.log("Dispatch the filter action");
        // Update ref
        sortRef.current = sortBy;
        // return after this line if you want to avoid next if condition;
      }
      if(filter !== filterRef.current){
        console.log("Dispatch the sort action");
        //Update ref
        filterRef.current = filter;
      }
    }
  });


  return <div>{products.map(product => <h1>{product}</h1>)}</div>
}