#reactjs #redux #react-redux
#reactjs #redux #react-redux
Вопрос:
В нашем приложении некоторые обновления данных поступают через сокеты. В обратных вызовах сокета отправляется действие redux. Вот пример
const onReceiveLocationMessages = function(message) {
let payload = JSON.parse(message.body);
console.log("received locations", payload);
dispatch(onLocationChange(payload));
};
Теперь иногда через сокет поступает сразу много обновлений, поэтому обратный вызов выполняется последовательно, поэтому также запускается много отправлений. Это приводит к отставанию приложения, например, при навигации, открытии меню, нажатии кнопок и т. Д., Потому что, как я предполагаю, происходит много рендеринга.
Я думаю, что-то похожее на batch
https://react-redux.js.org/api/batch из react-redux здесь помогло бы. Но, как показывает пример, он объединяет сразу несколько отправлений, и в данном случае это быстрые последовательные отправки.
Итак, какова наилучшая практика для обработки подобных ситуаций?
Комментарии:
1. Похоже, вам нужен какой-то буфер. здесь есть много потенциальных решений, может быть, взгляните на это github.com/rt2zz/redux-action-buffer если бы вы использовали что-то вроде redux-observable, вы могли бы использовать встроенные операторы, которые очень хорошо обрабатывают этот тип буферизации redux-observable.js.org
Ответ №1:
Вы уверены, что не выполняете ненужный рендеринг многих частей вашего приложения или не используете долго работающий редуктор, который вызывает задержку? Если вы используете чистые компоненты с reselect, вы можете предотвратить ненужный рендеринг компонентов и получить менее медленное приложение.
Ниже приведен пример вспомогательной функции с именем createBatchAction
, которая выполняет пакетные обновления. Вы передаете создателю действия (toggleColorBatched) помощнику и batchPeriod
параметр и возвращаете новый блок создания действия, который будет выполнять все собранные действия в течение периода пакета.
Существует еще один создатель действия с именем toggleColorNotBatched, который отправляется вместе с batchedToggleColorBatche
(новое действие thunk, созданное с помощью createBatchAction), но вы можете видеть, что пакетная версия обновляет DOM пакетно.
const {
Provider,
useDispatch,
useSelector,
batch,
} = ReactRedux;
const { createStore, applyMiddleware, compose } = Redux;
// helper to batch action
const createBatchAction = (actionCreator, batchPeriod) => {
const data = { dispatch: null, actions: [] };
setInterval(() => {
batch(() =>
data.actions.forEach((action) => data.dispatch(action))
);
data.actions = [];
}, batchPeriod);
return (...args) => (dispatch) => {
data.dispatch = dispatch;
data.actions.push(actionCreator(...args));
};
};
const ROWS = 10;
const COLS = 20;
const BLUE = 'blue';
const RED = 'red';
const initial = Array(ROWS)
.fill()
.map(() =>
Array(COLS)
.fill()
.map(() => ({ color: BLUE }))
);
const initialState = {
batched: initial,
notBatched: initial,
};
const nextItem = ((row, col) => () => {
col = 1;
if (col >= COLS) {
col = 0;
row = 1;
}
if (row >= ROWS) {
row = 0;
}
return { row, col };
})(0, -1);
//action types
const TOGGLE_COLOR_BATHCED = 'TOGGLE_COLOR_BATHCED';
const TOGGLE_COLOR_NOT_BATCHED = 'TOGGLE_COLOR_NOT_BATCHED';
//action creators
const toggleColorBatched = ({ row, col }) => ({
type: TOGGLE_COLOR_BATHCED,
payload: { row, col },
});
const toggleColorNotBatched = ({ row, col }) => ({
type: TOGGLE_COLOR_NOT_BATCHED,
payload: { row, col },
});
//batched version of toggleColor that batches all actions every second
const batchedToggleColorBatche = createBatchAction(
toggleColorBatched,
1000
);
const setToggle = (rows, row, col) =>
rows.map((val, i) =>
i === row
? val.map((val, i) =>
i === col
? {
...val,
color: val.color === BLUE ? RED : BLUE,
}
: val
)
: val
);
const reducer = (state, { type, payload }) => {
if (type === TOGGLE_COLOR_BATHCED) {
const { row, col } = payload;
return {
...state,
batched: setToggle(state.batched, row, col),
};
}
if (type === TOGGLE_COLOR_NOT_BATCHED) {
const { row, col } = payload;
return {
...state,
notBatched: setToggle(state.notBatched, row, col),
};
}
return state;
};
//selectors
const selectBatched = (state) => state.batched;
const selectNotBatched = (state) => state.notBatched;
//creating store with redux dev tools
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancers(
applyMiddleware(
//adding thunk like middleware
({ dispatch, getState }) => (next) => (action) =>
typeof action === 'function'
? action(dispatch, getState)
: next(action)
)
)
);
// Col only re renders if props passed to it change
const Col = React.memo(function Col({ col }) {
return <td style={{ color: col.color }}>X</td>;
});
// Row only re renders if props passed to it change
const Row = React.memo(function Row({ row }) {
return (
<tr>
{row.map((col, i) => (
<Col key={i} col={col} />
))}
</tr>
);
});
const App = () => {
const batched = useSelector(selectBatched);
const notBatched = useSelector(selectNotBatched);
const dispatch = useDispatch();
React.useEffect(() => {
const interval = setInterval(() => {
const item = nextItem();
dispatch(batchedToggleColorBatche(item));
dispatch(toggleColorNotBatched(item));
}, 50);
const item = nextItem();
dispatch(batchedToggleColorBatche(item));
dispatch(toggleColorNotBatched(item));
return () => clearInterval(interval);
}, [dispatch]);
return (
<table>
<tbody>
<tr>
<td>
<h1>batched</h1>
</td>
<td>
<h1>not batched</h1>
</td>
</tr>
<tr>
<td>
<table>
<tbody>
{batched.map((row, i) => (
<Row key={i} row={row} />
))}
</tbody>
</table>
</td>
<td>
<table>
<tbody>
{notBatched.map((row, i) => (
<Row key={i} row={row} />
))}
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
);
};
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>