I need to persist multiple states in Redux store using LocalStorage. I already have working one key in my case it is drivers. also need to do with buses and carriers states.
Store.js
import ReduxThunk from 'redux-thunk';
import { createStore, applyMiddleware, compose } from 'redux';
import Reducers from './reducers';
import { loadState, saveState } from '../../utils/localstorage';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// LOCAL STORAGE FOR DRIVERS, BUSES, CARRIERS
const persistedState = loadState()
const store = createStore(
Reducers,
persistedState,
composeEnhancers(
applyMiddleware(
ReduxThunk
)
)
);
store.subscribe(() => {
saveState({
drivers: store.getState().drivers
});
});
export default store;
localstorage.js
export const loadState = () => {
try {
const serializedDriversState = localStorage.getItem('drivers')
if (serializedDriversState === null) {
return undefined;
}
return JSON.parse(serializedDriversState);
} catch (err) {
return undefined;
}
};
export const saveState = (drivers) => {
try {
const serializedDriversState = JSON.stringify(drivers);
localStorage.setItem('drivers', serializedDriversState)
} catch (err) {
// Ignore errors.
}
}
I'm using Dan Abramov's example: Redux LocalStorage.
So how to store multiple states using LocalStorage in Redux store? Is it good approach or use some middleware like redux-persist?
I think, you should add forEach over state in store.subscribe function and you can save the whole store partly.
export const saveState = (key, data) => {
try {
const serialized = JSON.stringify(data);
localStorage.setItem(key, serialized);
} catch (err) {
// Ignore errors.
}
}
store.subscribe(() => {
const state = store.getState();
Object.keys(state).forEach( key => {
saveState(key, state[key])
})
});
Related
I'm new to react-redux and have some trouble persisting the state, mainly problem with loading the state from localstorage. I can save data to localstorage with no problem, however the data inside redux dev tools resets on refresh. The data in localstorage is as it should be.
I speculate that the loadstate function is not working properly, thus not correctly fetching data from localstorage.
The reason for using (key, data) rather than (state) is that i don't get the error "objects are not valid as a react child", but it should work as good as using (state).
My localstorage.js
export const saveState = (key, data) => {
try {
const serialized = JSON.stringify(data);
localStorage.setItem(key, serialized);
} catch (err) {
// Ignore errors.
}
}
export const loadState = () => {
try {
const serializedState = localStorage.getItem('state');
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
my subscribe method:
store.subscribe(() => {
const state = store.getState();
Object.keys(state).forEach(
key => {saveState(key, state[key])}
)
})
My store class:
import allReducers from './reducers'
import {createStore} from 'redux';
import { loadState } from './localStorage';
export const store = createStore(allReducers, loadState(), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
It’s because you are trying to get from storage entire state but you are saving it by keys that’s why loadState is not returning state. Try something like this
store.subscribe(() => {
const state = store.getState();
saveState('state', state);
});
I have looked into multiple sources trying to solve this problem but could not find any answers. I have a functional component <Dashboard /> which will display some information from an API.
I expected the component to first get into useEffect, execute the getData function and then display {devices} on the screen. What happens, though, is that the store state is updated, but the component not. The {devices} variable is always undefined. I don't think I understand how to access my state variable from reducers/all/dashboard.js with useSelector.
dashboard/index.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import api from "../../services/api";
import * as DashboardActions from "../../store/actions/dashboard";
const Dashboard = (props) => {
const dispatch = useDispatch();
const devices = useSelector(state => state.device)
useEffect(() => {
async function getData() {
const pathname = "/dashboard";
await api
.get(pathname)
.then((res) => {
dispatch(DashboardActions.setData(res.data));
})
.catch((res) => {
console.log(res.response.data);
});
}
getData();
console.log("devices ue ", devices);
}, [dispatch]);
return (
<div>
<h1>Dashboard</h1>
<span>{devices}</span>
</div>
);
};
export default Dashboard;
reducers/all/dashboard.js
const INITIAL_STATE = {
devices: [],
};
function dashboard(state = INITIAL_STATE, action) {
console.log("Action ", action)
if ("DASHBOARD_SET_DATA" === action.type) {
const data = action.data;
console.log("Data: ", data.devices)
state = { ...state, devices: data.devices };
console.log("State ", state)
}
return state;
}
export default dashboard;
actions/dashboard.js
export function setData(data) {
return {
type: "DASHBOARD_SET_DATA",
data,
};
}
I would appreciate any help a lot.
Thanks in advance!
The react-redux useSelector hook is selecting state from your redux store state object.
If your dashboard reducer is combined into your root reducer, something like
const rootReducer = combineReducers({
... other reducers
dashboard,
... other reducers
});
Then the devices state value should be accessed from state.dashboard.devices.
The update for your component:
const devices = useSelector(state => state.dashboard.devices)
The Redux Update operations I made on the client-side in Next JS are not updated in the server store.
Hello. I have a problem. I'm developing an SSR application with Next JS. I have provided the next js link with the next-redux-wrapper. State update operations can be provided. State updates I made on the server-side can be viewed on the client-side. The redux updates I made on the client-side also appear on the client-side, but when I refresh the page, it returns to the previous position. Sample scenario:
Users have addresses. Their addresses can be taken from the DB and printed on the screen. DB updates when I add a new address or delete the old address. Along with it, it is updated in the store on the client-side. So far there is no problem. However, when I refresh the page, for example, if there are 4 addresses before updating and I deleted one, after the refresh, it is printed as 4 addresses again. It continues like this until I get data from the server again.
How can I move the client-side store updates to the server-side without having to make requests to the server over and over again?
store.js
// store.js
import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from "next-redux-wrapper";
import thunkMiddleware from 'redux-thunk'
// ROOT REDUCERS
import rootReducer from "../reducers";
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
const store_ = (initialState) => {
return createStore(rootReducer, initialState, bindMiddleware([thunkMiddleware]));
}
const wrapper = createWrapper(store_/*, { debug: true }*/);
export {
wrapper
}
_app.js
// _app.js
const MyApp = ({props, Component, pageProps }) => {
const store = useStore();
if (!store.getState().R_PageSettings.initStore)
{
store.dispatch({
type: HYDRATE,
payload: {
...props.initialState
}
})
}
return (
<>
<Head>
<title>{ variables.meta.title }</title>
</Head>
<Component {...pageProps} />
</>
)
}
const wrappedApp = wrapper.withRedux(MyApp);
export default wrappedApp;
wrappedApp.getInitialProps = async ctx => {
const data = await wrapper.getServerSideProps(
async (req) => {
const { store, ctx } = req;
const reduxStates = store.getState();
let user = reduxStates.R_User.user;
if (!user)
{
const cookies = parseCookies(ctx);
if (cookies.usr && user !== undefined)
{
const getUser = await CustomersController.tokenLoginControl(cookies.usr);
if (getUser && getUser.status)
{
store.dispatch(setUserSSR(getUser.user))
user = getUser.user;
}
else
destroyCookie(ctx, 'usr');
}
}
return {
user
}
}
)(ctx)
return data;
}
action.js
// CONSTANTS
import {
C_User
} from "../constants";
export const setUserSSR = user => {
return {
type: C_User.SET_USER,
payload: {
user
}
}
}
export const setUser = user => dispatch => {
return dispatch({
type: C_User.SET_USER,
payload: {
user
}
})
}
addresspage.js
// addresspage.js
import { connect } from 'react-redux';
import { bindActionCreators } from "redux";
// COMPONENTS
import UserPageLayout from "../UserPagesLayout";
import {
CustomerAddressForm
} from "../../../components";
// CONTROLLERS
import {
CustomersController
} from "../../../controllers";
// ACTIONS
import {
setUser
} from "../../../actions";
const MyAddressPage = connect(({ R_User }) => {
return {
R_User
}
}, dispatch => {
return {
setUser: bindActionCreators(setUser, dispatch)
}
})((props) => {
const addAddressHandle = () => {
props.fullBarOpen(
<CustomerAddressForm confirmHandle={async (address, setLoading) => {
const execute = await CustomersController.addAddress(address);
if (execute.status)
{
await props.setUser(execute.user);
}
else
{
setLoading(false);
}
}}
/>
);
}
return (
<UserPageLayout>
</UserPageLayout>
);
})
export default MyAddressPage;
I have been able to create a persistent local storage that allows the user to login and stores a randomly generated API token, which is necessary to communicate with the API.
The issue that I am having, is that my sign out button does not work anymore because the session store keeps the login information and token in the local storage and doesn't delete unless I manually do it.
Below is my code.
import { createStore, applyMiddleware, compose } from "redux";
import rootReducer from "../reducers";
import thunk from "redux-thunk";
function saveToLocalStorage(state) {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem("state", serializedState);
} catch (e) {
console.log(e);
}
}
function loadFromLocalStorage() {
try {
const serializedState = localStorage.getItem("state");
if (serializedState === null) return undefined;
return JSON.parse(serializedState);
} catch (e) {
console.log(e);
return undefined;
}
}
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|| compose;
const persistedState = loadFromLocalStorage();
const store = createStore(
rootReducer,
persistedState,
storeEnhancers(applyMiddleware(thunk))
);
store.subscribe(() => saveToLocalStorage(store.getState()));
export default store;
I have already a localStorage code for my web project.
My localStorage file for save and load localStorage.
// localStorage.js
export const loadState = () => {
try {
localStorage.clear();
const version = localStorage.getItem("version");
// you can update the version to init the persist state
if (version !== "0.1") {
localStorage.clear();
localStorage.setItem("version", "0.1");
}
const serializedState = localStorage.getItem("state");
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
export const saveState = state => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem("state", serializedState);
} catch (err) {
// Ignore write errors.
}
};
And my store file to init the redux store.
// store.js
import persistReducer from "./reducers/persistReducer";
...
import { loadState, saveState } from "./localStorage";
const persistedState = loadState();
const store = createStore(
combineReducers({
persist: persistReducer,
...
}),
persistedState,
composeWithDevTools(applyMiddleware(thunk))
);
store.subscribe(
throttle(() => {
saveState({
persist: store.getState().persist
});
}, 1000)
);
ReactDOM.render(
<Provider store={store}>
...
</Provider>,
document.getElementById("root")
);
registerServiceWorker();
Now I want to migrate this code to AsyncStorage, how could simply change this code to adjust for my native app project?