Как использовать реакционный хук useEffect только один раз в моем случае? - PullRequest
2 голосов
/ 26 октября 2019

Я думаю, что мой вопрос на некоторое время кажется дубликатом, но я думаю, что это не так, я проверил много вопросов, имеющих одинаковое название, но с другим регистром

Проблема в том, что я был вынужден использоватьреагировать ловушкой дважды в моем коде, и я даже думаю сделать их тремя, но я чувствую, что это плохая практика, и я уверен, что есть обходной путь или решение для этого

У меня есть данные, которые я хочу первоначально получитьи я реализовал, что при первом вызове useEffect данные могут быть отфильтрованы или отсортированы позже, и у меня есть флажки фильтров и выбор для запросов сортировки, флажок будет фильтровать данные, а выбор опции будет сортировать их,Я использую метод redux и response-redux connect для создания дерева состояний, которое включает filters и sortBy

  //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])

и это потому, что я не хочу, чтобы фильтрация и сортировка происходили каждый раз, даже когда должен работать только один из них, потому что я думаю, что сортировка будет работать и тогда, когда пользователь только подбирает данные, и наоборот.

Этоmy 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 ]

3 голосов
/ 26 октября 2019

В fetchProducts вы изменяете продукты, и это не очень хорошая идея, вместо этого попробуйте products = sortProducts([...products], sortBy);.

Если начальное значение state.filters равно [], а начальное значение sortBy равно false или работаетсортировка по умолчанию, тогда вам нужен только один эффект.

Как прокомментировал скайбойер;если вы выполняете асинхронную обработку, когда пользователь меняет фильтр или сортировку, вам следует избегать условие гонки .

Вот как можно написать эффект:

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, тогда это должно сработать.

2 голосов
/ 26 октября 2019

Если вы хотите обработать всю логику в одном вызове useEffect, вы можете удалить все зависимости реквизита [filters, sortBy] из useEffect.

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>
}
...