Tôi muốn duy trì một số phần của cây trạng thái của mình cho localStorage. Nơi thích hợp để làm như vậy là gì? Giảm hay hành động?
Tôi muốn duy trì một số phần của cây trạng thái của mình cho localStorage. Nơi thích hợp để làm như vậy là gì? Giảm hay hành động?
Câu trả lời:
Reducer không bao giờ là một nơi thích hợp để làm điều này bởi vì các chất làm giảm nên tinh khiết và không có tác dụng phụ.
Tôi khuyên bạn chỉ nên làm điều đó trong một thuê bao:
store.subscribe(() => {
// persist your state
})
Trước khi tạo cửa hàng, hãy đọc những phần vẫn tồn tại:
const persistedState = // ...
const store = createStore(reducer, persistedState)
Nếu bạn sử dụng, combineReducers()bạn sẽ nhận thấy rằng các bộ giảm tốc chưa nhận được trạng thái sẽ khởi động lại như bình thường bằng cách sử dụng stategiá trị đối số mặc định của chúng . Điều này có thể khá tiện dụng.
Bạn nên gỡ bỏ thuê bao của mình để không viết thư cho localStorage quá nhanh, hoặc bạn sẽ gặp vấn đề về hiệu suất.
Cuối cùng, bạn có thể tạo một phần mềm trung gian đóng gói nó như một giải pháp thay thế, nhưng tôi bắt đầu với một người đăng ký vì đó là một giải pháp đơn giản hơn và thực hiện công việc tốt.
Để điền vào chỗ trống trong câu trả lời của Dan Abramov, bạn có thể sử dụng store.subscribe()như thế này:
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
Trước khi tạo cửa hàng, hãy kiểm tra localStoragevà phân tích bất kỳ JSON nào dưới khóa của bạn như thế này:
const persistedState = localStorage.getItem('reduxState')
? JSON.parse(localStorage.getItem('reduxState'))
: {}
Sau đó, bạn truyền persistedStatehằng số này cho createStorephương thức của bạn như thế này:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
persistedStatetrả về initialStatethay vì một đối tượng trống không? Nếu không tôi nghĩ createStoresẽ khởi tạo với đối tượng trống rỗng đó.
Trong một từ: phần mềm trung gian.
Kiểm tra redux-kiên trì . Hoặc viết cho riêng bạn.
[CẬP NHẬT ngày 18 tháng 12 năm 2016] Đã chỉnh sửa để xóa đề cập đến hai dự án tương tự hiện không hoạt động hoặc không dùng nữa.
Nếu bất cứ ai đang có bất kỳ vấn đề với các giải pháp trên, bạn có thể viết riêng của bạn. Hãy để tôi chỉ cho bạn những gì tôi đã làm. Bỏ qua saga middlewaremọi thứ chỉ tập trung vào hai điều localStorageMiddlewarevà reHydrateStorephương pháp. các localStorageMiddlewarekéo tất cả các redux statevà đặt nó trong local storagevà rehydrateStorekéo tất cả các applicationStatetrong lưu trữ địa phương nếu hiện tại và đặt nó trongredux store
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga';
import decoristReducers from '../reducers/decorist_reducer'
import sagas from '../sagas/sagas';
const sagaMiddleware = createSagaMiddleware();
/**
* Add all the state in local storage
* @param getState
* @returns {function(*): function(*=)}
*/
const localStorageMiddleware = ({getState}) => { // <--- FOCUS HERE
return (next) => (action) => {
const result = next(action);
localStorage.setItem('applicationState', JSON.stringify(
getState()
));
return result;
};
};
const reHydrateStore = () => { // <-- FOCUS HERE
if (localStorage.getItem('applicationState') !== null) {
return JSON.parse(localStorage.getItem('applicationState')) // re-hydrate the store
}
}
const store = createStore(
decoristReducers,
reHydrateStore(),// <-- FOCUS HERE
applyMiddleware(
sagaMiddleware,
localStorageMiddleware,// <-- FOCUS HERE
)
)
sagaMiddleware.run(sagas);
export default store;
localStoragengay cả khi không có gì trong cửa hàng thay đổi? Cách bạn bù đắp cho việc ghi không cần thiết
Tôi không thể trả lời @Gardezi nhưng một tùy chọn dựa trên mã của anh ấy có thể là:
const rootReducer = combineReducers({
users: authReducer,
});
const localStorageMiddleware = ({ getState }) => {
return next => action => {
const result = next(action);
if ([ ACTIONS.LOGIN ].includes(result.type)) {
localStorage.setItem(appConstants.APP_STATE, JSON.stringify(getState()))
}
return result;
};
};
const reHydrateStore = () => {
const data = localStorage.getItem(appConstants.APP_STATE);
if (data) {
return JSON.parse(data);
}
return undefined;
};
return createStore(
rootReducer,
reHydrateStore(),
applyMiddleware(
thunk,
localStorageMiddleware
)
);
sự khác biệt là chúng tôi chỉ lưu một số hành động, bạn có thể sử dụng chức năng gỡ lỗi để chỉ lưu tương tác cuối cùng của trạng thái của bạn
Tôi hơi muộn nhưng tôi đã thực hiện một trạng thái liên tục theo các ví dụ được nêu ở đây. Nếu bạn muốn cập nhật trạng thái chỉ sau mỗi X giây, phương pháp này có thể giúp bạn:
Xác định chức năng bao bọc
let oldTimeStamp = (Date.now()).valueOf()
const millisecondsBetween = 5000 // Each X milliseconds
function updateLocalStorage(newState)
{
if(((Date.now()).valueOf() - oldTimeStamp) > millisecondsBetween)
{
saveStateToLocalStorage(newState)
oldTimeStamp = (Date.now()).valueOf()
console.log("Updated!")
}
}Gọi một chức năng bao bọc trong thuê bao của bạn
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});Trong ví dụ này, trạng thái được cập nhật tối đa cứ sau 5 giây, bất kể tần suất cập nhật được kích hoạt.
(state) => { updateLocalStorage(store.getState()) }trong lodash.throttlenhư thế này: store.subscribe(throttle(() => {(state) => { updateLocalStorage(store.getState())} }và loại bỏ thời gian kiểm tra bên trong logic.
Dựa trên các đề xuất tuyệt vời và đoạn trích mã ngắn được cung cấp trong các câu trả lời khác (và bài viết Trung bình của Jam Creencia ), đây là một giải pháp hoàn chỉnh!
Chúng tôi cần một tệp chứa 2 hàm lưu / tải trạng thái đến / từ bộ nhớ cục bộ:
// FILE: src/common/localStorage/localStorage.js
// Pass in Redux store's state to save it to the user's browser local storage
export const saveState = (state) =>
{
try
{
const serializedState = JSON.stringify(state);
localStorage.setItem('state', serializedState);
}
catch
{
// We'll just ignore write errors
}
};
// Loads the state and returns an object that can be provided as the
// preloadedState parameter of store.js's call to configureStore
export const loadState = () =>
{
try
{
const serializedState = localStorage.getItem('state');
if (serializedState === null)
{
return undefined;
}
return JSON.parse(serializedState);
}
catch (error)
{
return undefined;
}
};
Các hàm này được nhập bởi store.js nơi chúng tôi định cấu hình cửa hàng của chúng tôi:
LƯU Ý: Bạn sẽ cần thêm một phụ thuộc: npm install lodash.throttle
// FILE: src/app/redux/store.js
import { configureStore, applyMiddleware } from '@reduxjs/toolkit'
import throttle from 'lodash.throttle';
import rootReducer from "./rootReducer";
import middleware from './middleware';
import { saveState, loadState } from 'common/localStorage/localStorage';
// By providing a preloaded state (loaded from local storage), we can persist
// the state across the user's visits to the web app.
//
// READ: https://redux.js.org/recipes/configuring-your-store
const store = configureStore({
reducer: rootReducer,
middleware: middleware,
enhancer: applyMiddleware(...middleware),
preloadedState: loadState()
})
// We'll subscribe to state changes, saving the store's state to the browser's
// local storage. We'll throttle this to prevent excessive work.
store.subscribe(
throttle( () => saveState(store.getState()), 1000)
);
export default store;
Cửa hàng được nhập vào index.js để có thể chuyển nó vào Nhà cung cấp bao bọc App.js :
// FILE: src/index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import App from './app/core/App'
import store from './app/redux/store';
// Provider makes the Redux store available to any nested components
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Lưu ý rằng nhập tuyệt đối yêu cầu thay đổi này đối với YourProjectFolder / jsconfig.json - điều này cho biết nơi tìm tệp nếu nó không thể tìm thấy chúng lúc đầu. Mặt khác, bạn sẽ thấy các khiếu nại về việc cố gắng nhập nội dung nào đó từ bên ngoài src .
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}