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 state
giá 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 localStorage
và 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 persistedState
hằng số này cho createStore
phương thức của bạn như thế này:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
persistedState
trả về initialState
thay vì một đối tượng trống không? Nếu không tôi nghĩ createStore
sẽ 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 middleware
mọi thứ chỉ tập trung vào hai điều localStorageMiddleware
và reHydrateStore
phương pháp. các localStorageMiddleware
kéo tất cả các redux state
và đặt nó trong local storage
và rehydrateStore
kéo tất cả các applicationState
trong 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;
localStorage
ngay 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.throttle
như 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"]
}