Компонент React не получает значение текущего состояния в функции прослушивателя событий

#reactjs #redux

#reactjs #redux

Вопрос:

Я подключил прослушиватель событий при монтировании компонента tabularlistpgin.

Затем я попытался получить доступ к текущему состоянию Redux в прослушивателе событий. Тем не менее, я получаю начальное состояние DataTable reducer вместо текущего состояния.

То же самое отлично работало в компоненте на основе классов. Не могли бы вы поделиться своим пониманием этого?

Извините за мое короткое на английском.

 import React, { useState, useEffect, useRef } from "react";
import { useSelector, useDispatch } from "react-redux";
import Paginate from "react-paginate";
import { loadDataTable } from "../../actions/index";

const TabularListPagination = () => {
    const dispatch = useDispatch();
    const dataTable = useSelector((state) => state.dataTable);

    useEffect(() => {
        attachShortcutEvent();
    }, []);

    const attachShortcutEvent = () => {
        document.addEventListener("keydown", (event) => {
            if (event.altKey amp;amp; event.key === "ArrowLeft") {
                if (dataTable.current.current_page !== 1) {
                    dispatch(loadDataTable(dataTable.current.current_page - 1));
                }
            } else if (event.altKey amp;amp; event.key === "ArrowRight") {
                if (dataTable.current.current_page !== dataTable.current.total_pages) {
                    dispatch(loadDataTable(dataTable.current.current_page   1));
                }
            } else if (event.altKey amp;amp; event.key === "Home") {
                dispatch(loadDataTable(1));
            } else if (event.altKey amp;amp; event.key === "End") {
                dispatch(loadDataTable(dataTable.current.total_pages));
            }
        });
    };

    return (
        <Paginate
            containerClassName="pagination"
            forcePage={dataTable.current_page}
            pageCount={dataTable.total_pages}
            marginPagesDisplayed={2}
            pageRangeDisplayed={5}
            onPageChange={({ selected }) => {
                dispatch(loadDataTable(selected   1));
            }}
            nextLabel="amp;>"
            previousLabel="amp;<"
        />
    );
};

export default TabularListPagination;
  

Комментарии:

1. У вас должно быть предупреждение об отсутствии зависимостей в эффекте. Было бы лучше не зависеть dataTable.current.current_page и выполнять вызываемое действие go , которое вы можете вызвать с помощью -1 для предыдущей страницы и 1 для следующей страницы, и обрабатывать логику в редукторе. Теперь ваш attachShortcutEvent зависит только от dataTable.current.total_pages so, поэтому создайте attachShortcutEvent с помощью useCallback и добавьте attachShortcutEvent к зависимости эффекта. Также очистите, чтобы добавить и удалить прослушиватель событий key up в эффекте.

Ответ №1:

Вот пример того, как реализовать в соответствии с моим комментарием:

 const { Provider, useDispatch, useSelector } = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
const { createSelector } = Reselect;

const initialState = {
  dataTable: {
    current: { total_pages: 10, current_page: 1 },
  },
};
//action types
const GO = 'GO';
const FIRST = 'FIRST';
const LAST = 'LAST';
//action creators
const go = (direction) => ({
  type: GO,
  payload: direction,
});
const first = () => ({ type: FIRST });
const last = () => ({ type: LAST });
const reducer = (state, { type, payload }) => {
  if (type === GO) {
    const current_page =
      state.dataTable.current.current_page   payload;
    if (
      current_page < 1 ||
      current_page > state.dataTable.current.total_pages
    ) {
      return state;
    }
    return {
      ...state,
      dataTable: {
        ...state.dataTable,
        current: {
          ...state.dataTable.current,
          current_page,
        },
      },
    };
  }
  if (type === FIRST || type === LAST) {
    const current_page =
      type === FIRST
        ? 1
        : state.dataTable.current.total_pages;
    return {
      ...state,
      dataTable: {
        ...state.dataTable,
        current: {
          ...state.dataTable.current,
          current_page,
        },
      },
    };
  }
  return state;
};
//selectors
const selectDataTable = (state) => state.dataTable;
const selectCurrentDataTable = createSelector(
  [selectDataTable],
  (table) => table.current
);
//creating store with redux dev tools
const composeEnhancers =
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  reducer,
  initialState,
  composeEnhancers(
    applyMiddleware(() => (next) => (action) =>
      next(action)
    )
  )
);
const App = () => {
  const dispatch = useDispatch();
  const dataTable = useSelector(selectCurrentDataTable);

  const keyUp = React.useCallback(
    (event) => {
      //dispatching the actions are not depending on state
      if (event.altKey amp;amp; event.key === 'ArrowLeft') {
        dispatch(go(-1));
      } else if (
        event.altKey amp;amp;
        event.key === 'ArrowRight'
      ) {
        dispatch(go(1));
      } else if (event.altKey amp;amp; event.key === 'Home') {
        dispatch(first());
      } else if (event.altKey amp;amp; event.key === 'End') {
        dispatch(last());
      }
      //only dep is dispatch but that never changes so keyUp is only
      //  created when component mounts. Added to dependency to silence
      //  linter (maybe updated version won't warn about dispatch)
    },
    [dispatch]
  );

  React.useEffect(() => {
    document.addEventListener('keydown', keyUp);
    //remove event listener when component is unmounted
    return () => document.removeEventListener(keyUp);
    //keyUp is a dependency but is only created on mount
  }, [keyUp]);

  return (
    <div>
      current page: {dataTable.current_page}
      total pages: {dataTable.total_pages}
    </div>
  );
};

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.2.0/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
<div id="root"></div>