#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>