Реагируйте на разбиение хуков с помощью useEffect - как вернуть номер страницы, если запрос не выполнен? - PullRequest
2 голосов
/ 01 апреля 2020

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

Псевдокод:

function reducer(state, action) {
  switch (action.type) {
    case 'next':
      return {page: state.page + 1};
    case 'prev':
      return {page: state.page - 1};
    default:
      throw new Error();
  }
}

function Page() {
  const [state, dispatch] = useReducer(reducer, { page: 1});

  useEffect(() => {
    loadMore(state.page)
    .then(r => console.log(r))
    .catch(e => {
      // how to revert page without triggering this effect?
      // cause if I do dispatch({type: 'prev'}) page will change, and this will re-run
      console.log(e)
    })
  }, state.page)

  return (
    <>
      Page: {state.page}
      <button onClick={() => dispatch({type: 'prev'})}>-</button>
      <button onClick={() => dispatch({type: 'next'})}>+</button>
    </>
  );
}

Давайте рассмотрим этот сценарий :

  • мы начинаем со страницы 1
  • пользователь нажимает далее
  • страница увеличивается в состоянии
  • useEffect запускается, сетевой запрос не выполняется.
  • Страница в состоянии 2, но фактические данные имеют страницу 1

Я не могу отправить событие изменения страницы при перехвате, потому что это вызовет повторное получение, что я не хочу делать

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

Ответы [ 3 ]

2 голосов
/ 01 апреля 2020

Вы также можете просто удалить useEffect и использовать вместо этого такую ​​функцию:

let fetch = (page, isNext)=>{

 loadMore(page)
    .then(r => {
      // ..
      isNext ? dispatch({type: 'next'}):dispatch({type: 'prev'});
    })
    .catch(e => {
      // Don't dispatch anything, you will remain on same page
    })

}

Теперь, когда пользователь нажимает следующий, просто позвоните по этому fetch(state.page + 1, true). Если она нажимает prev btn, используйте fetch(state.page - 1, false);

0 голосов
/ 02 апреля 2020

Я бы использовал useRef(), чтобы сохранить предыдущую страницу в случае успеха. Если текущий вызов не выполняется, go вернитесь на предыдущую страницу, установив его. При вводе useEffect() проверьте, совпадают ли текущая страница и страница, сохраненная в ссылке, и ничего не делайте, если они совпадают.

function reducer(state, { type, page }) {
  switch (type) {
    case 'set':
      return { page };
    case 'next':
      return { page: state.page + 1 };
    case 'prev':
      return { page: state.page - 1 };
    default:
      throw new Error();
  }
}

function Page() {
  const [state, dispatch] = useReducer(reducer, { page: 1 });

  const currentPage = useRef();

  useEffect(() => {
    if (currentPage.current === state.page) return;

    loadMore(state.page)
    .then(r => {
      currentPage.current = state.page;
    })
    .catch(e => {
      dispatch({ type: 'set', page: currentPage.current || 1 }); // if currentPage.current doesn't exist use page 1 as default

      console.log(e)
    })
  }, state.page)

  return (
    <>
      Page: {state.page}
      <button onClick={() => dispatch({type: 'prev'})}>-</button>
      <button onClick={() => dispatch({type: 'next'})}>+</button>
    </>
  );
}
0 голосов
/ 01 апреля 2020

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

function reducer(state, action) {
  switch (action.type) {
    case 'failed':
      return { ...state, requestedPage: null };
    case 'loaded':
      return { page: action.page, requestedPage: null };
    case 'next':
      return { ...state, requestedPage: state.page + 1 };
    case 'prev':
      return { ...state, requestedPage: state.page - 1 };
    default:
      throw new Error();
  }
}

const Page = () => {
  const [{ page, requestedPage }, dispatch] = useReducer(reducer, {
    page: 1,
    requestedPage: null,
  });

  useEffect(() => {
    loadMore(requestedPage)
      .then((r) => {
        dispatch({ type: 'loaded', page: requestedPage });
        console.log(r);
      })
      .catch((e) => {
        dispatch({ type: 'failed' });
        console.error(e);
      });
    // Make sure the dependencies are an array
  }, [requestedPage]);

  // Optimistically render the requestedPage (with a spinner?) if present,
  // otherwise current page.
  return (
    <>
      Page: {requestedPage || page}
      <button onClick={() => dispatch({ type: 'prev' })}>-</button>
      <button onClick={() => dispatch({ type: 'next' })}>+</button>
    </>
  );
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...