Redux uygulamasında localStorage'a nerede yazılır?


Yanıtlar:


224

Redüktör bunu yapmak için asla uygun bir yer değildir çünkü redüktörler saf olmalı ve yan etkisi olmamalıdır.

Ben sadece bir abone yapmak tavsiye ederim:

store.subscribe(() => {
  // persist your state
})

Mağazayı oluşturmadan önce, bu kalıcı bölümleri okuyun:

const persistedState = // ...
const store = createStore(reducer, persistedState)

Eğer kullanırsanız combineReducers()normal olarak varsayılan kullanarak “yukarı önyükleme” olacak devlet almadıklarını düşürücüler fark edeceksiniz stateargüman değerini. Bu oldukça kullanışlı olabilir.

LocalStorage'a çok hızlı yazmamanız için abonenizi geri almanız veya performans sorunlarınız olması önerilir.

Son olarak, alternatif olarak kapsülleyen bir ara katman yazılımı oluşturabilirsiniz, ancak daha basit bir çözüm olduğu ve işi iyi yaptığı için bir abone ile başlayacağım.


1
ne Eyaletin sadece bir kısmına abone olmak istersem? mümkün mü? Biraz farklı bir şeyle devam ettim: 1. kalıcı durumu redux dışında kurtarın. 2. kalıcı durumu reaksiyon yapıcısına (veya componentWillMount ile) redux eylemiyle yükleyin. Kalıcı verileri doğrudan mağazaya yüklemek yerine 2 tamamen iyi mi? (SSR için ayrı tutmaya çalışıyorum). Bu arada Redux için teşekkürler! Harika, seviyorum, büyük proje
kodumla

store.subscribe geri arama, mevcut mağaza verilerine tam erişiminiz vardır, böylece ilgilendiğiniz herhangi bir parçayı devam ettirebilirsiniz
Bo Chen

35
Dan Abramov ayrıca bir video hazırladı: egghead.io/lessons/…
NateW

2
Her mağaza güncellemesinde durumun serileştirilmesiyle ilgili meşru performans endişeleri var mı? Tarayıcı ayrı bir iş parçacığında seri hale geliyor olabilir mi?
Stephen Paul

7
Bu potansiyel olarak savurgan görünüyor. OP, devletin sadece bir kısmının devam etmesi gerektiğini söyledi. Diyelim ki 100 farklı anahtarlı bir mağazanız var. Sadece seyrek olarak değiştirilen 3 tanesi ısrar etmek istiyorsun. Şimdi, 100 anahtarınızdan herhangi birindeki küçük değişikliklerde yerel mağazayı ayrıştırıyor ve güncelliyorsunuz, ancak devam etmekle ilgilenen 3 anahtardan hiçbiri bile değişmedi. @Gardezi tarafından Çözüm Aşağıdaki böylece size katman içine olay dinleyicileri ekleyebilirsiniz olarak daha iyi bir yaklaşımdır sadece : aslında örneğin gerektiğinde güncellemek codepen.io/retrcult/pen/qNRzKN
Anna T

153

Dan Abramov'un cevabının boşluklarını doldurmak için şöyle kullanabilirsiniz store.subscribe():

store.subscribe(()=>{
  localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})

Mağazayı oluşturmadan önce localStorage, anahtarınızın altındaki herhangi bir JSON'u aşağıdaki gibi kontrol edin ve ayrıştırın:

const persistedState = localStorage.getItem('reduxState') 
                       ? JSON.parse(localStorage.getItem('reduxState'))
                       : {}

Daha sonra bu persistedStatesabiti createStoreyönteminize şu şekilde aktarırsınız :

const store = createStore(
  reducer, 
  persistedState,
  /* any middleware... */
)

6
Ekstra bağımlılık olmadan basit ve etkili.
AxeEffect

1
bir ara katman oluşturma biraz daha iyi görünebilir, ancak etkili olduğunu ve çoğu durumda yeterli olduğunu kabul ediyorum
Bo Chen

CombineReducers kullanırken bunu yapmanın güzel bir yolu var mı ve sadece bir mağazaya devam etmek mi istiyorsunuz?
brendangibson

1
LocalStorage içinde hiçbir şey yoksa, boş bir nesne yerine persistedStatedönmeli initialStatemi? Aksi takdirde createStoreboş nesne ile başlayacağını düşünüyorum .
Alex,

1
@Alex initialState öğeniz boş olmadığı sürece haklısınız.
Link14

47

Tek kelimeyle: ara katman yazılımı.

Redux-persist'u kontrol edin . Veya kendiniz yazın.

[UPDATE 18 Ara 2016] Şu anda etkin olmayan veya kullanımdan kaldırılan benzer iki projeden bahsetmek için düzenlendi.


12

Yukarıdaki çözümlerle ilgili herhangi bir sorun varsa, kendinize yazabilirsiniz. Size ne yaptığımı göstereyim. saga middlewareŞeyleri görmezden gel, sadece iki şeye localStorageMiddlewareve reHydrateStoreyönteme odaklan . localStorageMiddlewareçekme hepsi redux stateve koyar onu local storageve rehydrateStoretüm çekme applicationStateyerel deposunda eğer mevcut ve koyar onu içinderedux 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;

2
Merhaba, bu localStorage, mağazadaki hiçbir şey değişmediğinde bile çok fazla yazımla sonuçlanmaz mı? Gereksiz yazmaları nasıl telafi edersiniz
user566245

Peki, işe yarıyor, neyse bu iyi fikre sahip olmak? Soru: Büyük veriye sahip birden fazla indirgeyiciye sahip daha fazla veri menümüz olması durumunda ne oldu?
Kunvar Singh

3

@Gardezi cevap veremem ama koduna dayalı bir seçenek olabilir:

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
    )
);

fark şu ki, sadece bazı eylemleri kaydediyoruz, olayınızın yalnızca son durumunuzun etkileşimini kaydetmek için bir geri dönme işlevi kullanabilirsiniz.


1

Biraz geç kaldım ama burada belirtilen örneklere göre kalıcı bir durum uyguladım. Durumu yalnızca her X saniyede bir güncellemek istiyorsanız, bu yaklaşım size yardımcı olabilir:

  1. Sarıcı işlevini tanımlama

    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!")
        }
    }
  2. Abonenizdeki bir sarma işlevini çağırın

        store.subscribe((state) =>
        {
        updateLocalStorage(store.getState())
         });

Bu örnekte, güncellemenin ne sıklıkta tetiklendiğine bakılmaksızın , durum en fazla her 5 saniyede bir güncellenir.


1
Sen tamamlayabileceğini (state) => { updateLocalStorage(store.getState()) }de lodash.throttleböyle: store.subscribe(throttle(() => {(state) => { updateLocalStorage(store.getState())} }ve mantık içini kontrol süresini kaldırın.
GeorgeMA

1

Diğer yanıtlarda (ve Jam Creencia'nın Medium makalesinde ) sağlanan mükemmel öneriler ve kısa kod alıntıları üzerine, tam bir çözüm!

Durumu yerel depolama birimine kaydeden / yerel depolama biriminden yükleyen 2 işlev içeren bir dosyaya ihtiyacımız var:

// 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;
  }
};

Bu işlevler mağazamızı yapılandırdığımız store.js tarafından içe aktarılır :

NOT: Bir bağımlılık eklemeniz gerekir: 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;

Mağaza index.js'ye aktarılır, böylece App.js'yi saran Sağlayıcıya aktarılabilir :

// 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')
)

Mutlak içe aktarma işlemlerinin YourProjectFolder / jsconfig.json için bu değişikliği gerektirdiğini unutmayın . Aksi takdirde, src dışından bir şey içe aktarmaya çalışmayla ilgili şikayetler görürsünüz .

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.