I want to use the client of react-apollo to reset part of the cache from a key or query, not clean the entirely cache using the client.resetStore().
Example:
import { withApollo } from "react-apollo";
import { compose } from "recompose";
import MyComponent from "./MyComponent";
const cleanEspecificCache = ({ client }) => () =>{
// What i do here?
}
const enhance = compose(
withApollo,
withHandlers({cleanEspecificCache})
)
export default enhance(MyComponent);
What i do to make it works? Thanks!
Partial Apollo Cache clearing is not supported at the moment as per this issue:
https://github.com/apollographql/apollo-feature-requests/issues/4
You can use fechPolicy: 'network-only' to not cache specific query at all:
const enhance = compose(
graphql(
gql`{
food {
id
name
}
}`,
{
options: { fetchPolicy: 'network-only' },
}
),
...
);
export default enhance(MyComponent);
If you want to go far down into rabbit hole and have a solution now, you can try https://github.com/sysgears/apollo-cache-router
it lets split your cache into two caches or more and then you can reset these caches individually:
import { InMemoryCache } from 'apollo-cache-inmemory';
import ApolloCacheRouter from 'apollo-cache-router';
import { hasDirectives } from 'apollo-utilities';
const generalCache = new InMemoryCache();
const specialCache = new InMemoryCache();
const cache = ApolloCacheRouter.override(
ApolloCacheRouter.route([generalCache, specialCache], document => {
if (hasDirectives(['special'], document)) {
// Pass all queries marked with #special into specialCache
return [specialCache];
} else {
// Pass all the other queries to generalCache
return [generalCache];
}
})
);
And whenever you want to reset specialCache call specialCache.reset() in the code.
Related
Originally, I was using new CounterStore inside React.createContext()
context.ts
import React from 'react'
import { stores, PersistState, CounterStore } from '#/store/index'
import type { ICounterStore } from '#/types/index'
export const FrameItContext = React.createContext<ICounterStore>(new CounterStore())
export const useCounterStore = () => React.useContext(FrameItContext)
Then I started using Mobx Persist Store in my app.
persist.ts
import { persistence, StorageAdapter } from 'mobx-persist-store'
import { CounterStore } from '#/store/index'
const read = (name: string): Promise<string> =>
new Promise((resolve) => {
const data = localStorage.getItem(name) || '{}'
console.log('got data: ', data)
resolve(data)
})
const write = (name: string, content: string): Promise<Error | undefined> =>
new Promise((resolve) => {
localStorage.setItem(name, content)
console.log('write data: ', name, content)
resolve(undefined)
})
export const PersistState = persistence({
name: 'CounterStore',
properties: ['counter'],
adapter: new StorageAdapter({ read, write }),
reactionOptions: {
// optional
delay: 2000,
},
})(new CounterStore())
And I changed my code to use PersistState instead of new CounterStore()
context.ts
import React from 'react'
import { stores, PersistState, CounterStore } from '#/store/index'
import type { ICounterStore } from '#/types/index'
export const FrameItContext = React.createContext<ICounterStore>(PersistState)
export const useCounterStore = () => React.useContext(FrameItContext)
It only logs got data: {} to the console. The write function never gets called.
Is there anything I am doing wrong?
Coincidentally, a simple Counter example on Codesandbox works perfectly fine → https://codesandbox.io/s/mobx-persist-store-4l1dm
The example above works on a simple Chrome extension or a web app but just doesn't seem to work with my specific application so I wrote a manual implementation of saving to LocalStorage.
Use toJSON() on the store to keep track of which properties should be saved:
toJSON() {
const { color, counter } = this
return {
color,
counter,
}
}
And add the localStorage logic just below the constructor(). First, check if the localStorage contains latest value & return it, if it does.
If there is nothing saved, then save it inside localStorage.
constructor() {
...
const name = "CounterStore"
const storedJson = localStorage.getItem(name)
if (storedJson) Object.assign(this, JSON.parse(storedJson))
autorun(() => {
localStorage.setItem(name, JSON.stringify(this))
})
}
Codesandbox → https://codesandbox.io/s/mobx-persist-store-manual-implementation-vm38r?file=/src/store.ts
Follow the official example to export your own useStore, and then use it in the component.
import { createStore, Store, useStore as baseUseStore } from 'vuex';
export const key: InjectionKey<Store<RootState>> = Symbol();
export function useStore() {
return baseUseStore(key);
}
use in the component
setup() {
const store = useStore();
const onClick = () => {
console.log(store)
store.dispatch('user/getUserInfo');
}
return {
onClick,
}
},
After running, store is undefined.
It can be obtained normally when I use it in the methods attribute
methods: {
login() {
this.$store.dispatch('user/getToken')
}
}
why? how to fix it
In that simplifying useStore usage tutorial, you still need to register the store and key in main.ts as they did. You will get undefined if you don't do this:
// main.ts
import { store, key } from './store'
const app = createApp({ ... })
// pass the injection key
app.use(store, key)
The reason is that baseUseStore(key) has no meaning until that's done.
Please, check the Edit
I'm trying to implement sagas in my app.
Right now I am fetching the props in a really bad way.
My app consists mainly on polling data from other sources.
Currently, this is how my app works:
I have containers which have mapStateToProps, mapDispatchToProps.
const mapStateToProps = state => {
return {
someState: state.someReducer.someReducerAction,
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({someAction, someOtherAction, ...}, dispatch)
};
const something = drizzleConnect(something, mapStateToProps, mapDispatchToProps);
export default something;
and then, I have actions, like this:
import * as someConstants from '../constants/someConstants';
export const someFunc = (someVal) => (dispatch) => {
someVal.methods.someMethod().call().then(res => {
dispatch({
type: someConstants.FETCH_SOMETHING,
payload: res
})
})
}
and reducers, like the below one:
export default function someReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case types.FETCH_SOMETHING:
return ({
...state,
someVar: action.payload
});
I combine the reducers with redux's combineReducers and export them as a single reducer, which, then, I import to my store.
Because I use drizzle, my rootSaga is this:
import { all, fork } from 'redux-saga/effects'
import { drizzleSagas } from 'drizzle'
export default function* root() {
yield all(
drizzleSagas.map(saga => fork(saga)),
)
}
So, now, when I want to update the props, inside the componentWillReceiveProps of the component, I do:
this.props.someAction()
Okay, it works, but I know that this is not the proper way. Basically, it's the worst thing I could do.
So, now, what I think I should do:
Create distinct sagas, which then I'll import inside the rootSaga file. These sagas will poll the sources every some predefined time and update the props if it is needed.
But my issue is how these sagas should be written.
Is it possible that you can give me an example, based on the actions, reducers and containers that I mentioned above?
Edit:
I managed to follow apachuilo's directions.
So far, I made these adjustments:
The actions are like this:
export const someFunc = (payload, callback) => ({
type: someConstants.FETCH_SOMETHING_REQUEST,
payload,
callback
})
and the reducers, like this:
export default function IdentityReducer(state = INITIAL_STATE, {type, payload}) {
switch (type) {
case types.FETCH_SOMETHING_SUCCESS:
return ({
...state,
something: payload,
});
...
I also created someSagas:
...variousImports
import * as apis from '../apis/someApi'
function* someHandler({ payload }) {
const response = yield call(apis.someFunc, payload)
response.data
? yield put({ type: types.FETCH_SOMETHING_SUCCESS, payload: response.data })
: yield put({ type: types.FETCH_SOMETHING_FAILURE })
}
export const someSaga = [
takeLatest(
types.FETCH_SOMETHING_REQUEST,
someHandler
)
]
and then, updated the rootSaga:
import { someSaga } from './sagas/someSagas'
const otherSagas = [
...someSaga,
]
export default function* root() {
yield all([
drizzleSagas.map(saga => fork(saga)),
otherSagas
])
}
Also, the api is the following:
export const someFunc = (payload) => {
payload.someFetching.then(res => {
return {data: res}
}) //returns 'data' of undefined but just "return {data: 'something'} returns that 'something'
So, I'd like to update my questions:
My APIs are depended to the store's state. As you may understood,
I'm building a dApp. So, Drizzle (a middleware that I use in order
to access the blockchain), needs to be initiated before I call
the APIs and return information to the components. Thus,
a. Trying reading the state with getState(), returns me empty contracts
(contracts that are not "ready" yet) - so I can't fetch the info - I
do not like reading the state from the store, but...
b. Passing the state through the component (this.props.someFunc(someState), returns me Cannot read property 'data' of undefined The funny thing is that I can console.log the
state (it seems okay) and by trying to just `return {data:
'someData'}, the props are receiving the data.
Should I run this.props.someFunc() on, for e.g., componentWillMount()? Is this the proper way to update the props?
Sorry for the very long post, but I wanted to be accurate.
Edit for 1b: Uhh, so many edits :)
I solved the issue with the undefined resolve. Just had to write the API like this:
export function someFunc(payload) {
return payload.someFetching.then(res => {
return ({ data: res })
})
}
I don't want to impose the pattern I use, but I've used it with success for awhile in several applications (feedback from anyone greatly appreciated). Best to read around and experiment to find what works best for you and your projects.
Here is a useful article I read when coming up with my solution. There was another, and if I can find it -- I'll add it here.
https://medium.com/#TomasEhrlich/redux-saga-factories-and-decorators-8dd9ce074923
This is the basic setup I use for projects.
Please note my use of a saga util file. I do provide an example of usage without it though. You may find yourself creating something along the way to help you reducing this boilerplate. (maybe even something to help handle your polling scenario).
I hate boilerplate so much. I even created a tool I use with my golang APIs to auto-generate some of this boilerplate by walking the swagger doc/router endpoints.
Edit: Added container example.
example component
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { getResource } from '../actions/resource'
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
getResource
},
dispatch
)
class Example extends Component {
handleLoad = () => {
this.props.getResource({
id: 1234
})
}
render() {
return <button onClick={this.handleLoad}>Load</button>
}
}
export default connect(
null,
mapDispatchToProps
)(Example)
example action/resource.js
import { useDispatch } from 'react-redux'
const noop = () => {}
const empty = []
export const GET_RESOURCE_REQUEST = 'GET_RESOURCE_REQUEST'
export const getResource = (payload, callback) => ({
type: GET_RESOURCE_REQUEST,
payload,
callback,
})
// I use this for projects with hooks!
export const useGetResouceAction = (callback = noop, deps = empty) => {
const dispatch = useDispatch()
return useCallback(
payload =>
dispatch({ type: GET_RESOURCE_REQUEST, payload, callback }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch, ...deps]
)
}
Fairly basic redux action file.
example reducers/resource.js
export const GET_RESOURCE_SUCCESS = 'GET_RESOURCE_SUCCESS'
const initialState = {
resouce: null
}
export default (state = initialState, { type, payload }) => {
switch (type) {
case GET_RESOURCE_SUCCESS: {
return {
...state,
resouce: payload.Data,
}
}
}
Fairly standard reducer pattern - NOTE the use of _SUCCESS instead of _REQUEST here. That's important.
example saga/resouce.js
import { takeLatest } from 'redux-saga/effects'
import { GET_RESOUCE_REQUEST } from '../actions/resource'
// need if not using the util
import { GET_RESOURCE_SUCCESS } from '../reducers/resource'
import * as resouceAPI from '../api/resource'
import { composeHandlers } from './sagaHandlers'
// without the util
function* getResourceHandler({ payload }) {
const response = yield call(resouceAPI.getResouce, payload);
response.data
? yield put({ type: GET_RESOURCE_SUCCESS, payload: response.data })
: yield put({
type: "GET_RESOURCE_FAILURE"
});
}
export const resourceSaga = [
// Example that uses my util
takeLatest(
GET_RESOUCE_REQUEST,
composeHandlers({
apiCall: resouceAPI.getResouce
})
),
// Example without util
takeLatest(
GET_RESOUCE_REQUEST,
getResourceHandler
)
]
Example saga file for some resource. This is where I wire up the api call with the reducer call in array per endpoint for the reosurce. This then gets spread over the root saga. Sometimes you may want to use takeEvery instead of takeLatest -- all depends on the use case.
example saga/index.js
import { all } from 'redux-saga/effects'
import { resourceSaga } from './resource'
export const sagas = [
...resourceSaga,
]
export default function* rootSaga() {
yield all(sagas)
}
Simple root saga, looks a bit like a root reducer.
util saga/sagaHandlers.js
export function* apiRequestStart(action, apiFunction) {
const { payload } = action
let success = true
let response = {}
try {
response = yield call(apiFunction, payload)
} catch (e) {
response = e.response
success = false
}
// Error response
// Edit this to fit your needs
if (typeof response === 'undefined') {
success = false
}
return {
action,
success,
response,
}
}
export function* apiRequestEnd({ action, success, response }) {
const { type } = action
const matches = /(.*)_(REQUEST)/.exec(type)
const [, requestName] = matches
if (success) {
yield put({ type: `${requestName}_SUCCESS`, payload: response })
} else {
yield put({ type: `${requestName}_FAILURE` })
}
return {
action,
success,
response,
}
}
// External to redux saga definition -- used inside components
export function* callbackHandler({ action, success, response }) {
const { callback } = action
if (typeof callback === 'function') {
yield call(callback, success, response)
}
return action
}
export function* composeHandlersHelper(
action,
{
apiCall = () => {}
} = {}
) {
const { success, response } = yield apiRequestStart(action, apiCall)
yield apiRequestEnd({ action, success, response })
// This callback handler is external to saga
yield callbackHandler({ action, success, response })
}
export function composeHandlers(config) {
return function*(action) {
yield composeHandlersHelper(action, config)
}
}
This is a very shortened version of my saga util handler. It can be a lot to digest. If you want the full version, I'll see what I can do. My full one handles stuff like auto-generating toast on api success/error and reloading certain resources upon success. Have something for handling file downloads. And another thing for handling any weird internal logic that might have to happen (rarely use this).
I am using a custom NextJS server with Apollo Client. I want to fetch the GraphQL data server-side and then send it to the client. I was kind of able to do that, but the client-side fetches it again. I understand that the Apollo cache is available only on the server, then needs to be sent to the client and restored from there.
The Apollo docs mention SSR but I don't want to fully render my app using the Apollo client, I want to use NextJS, I want to get just the data from the Apollo client and manually inject it into the HTML to restore it on the client. I looked at some examples for NextJS using Apollo, but none of them showed how to do exactly that.
This is my custom handler for requests:
const app = next({ dev: process.env.NODE_ENV !== 'production' });
const customHandler = async (req, res) => {
const rendered = await app.renderToHTML(req, res, req.path, req.query);
// somehow get the data from the apollo cache and inject it in the rendered html
res.send(rendered);
}
When you create the ApolloClient in the server, you can pass the initialState to hydrate the cache.
const createApolloClient = ({ initialState, headers }) =>
new ApolloClient({
uri: GRAPHQL_URL,
cache: new InMemoryCache().restore(initialState || {}) // hydrate cache
});
export default withApollo(PageComponent, { ssr = true } = {}) => {
const WithApollo = ({ apolloClient, apolloState, ...pageProps }) => {
const client = apolloClient || createApolloClient({ initialState: apolloState, headers: {} });
... rest of your code.
});
});
I have created a package just for this called nextjs-with-apollo. Take a look at https://github.com/adikari/nextjs-with-apollo. Once you have installed the package create a HOC as such.
// hocs/withApollo.js
import withApollo from 'nextjs-with-apollo';
import ApolloClient from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
const GRAPHQL_URL = 'https://your-graphql-url';
const createApolloClient = ({ initialState, headers }) =>
new ApolloClient({
uri: GRAPHQL_URL,
cache: new InMemoryCache().restore(initialState || {}) // hydrate cache
});
export default withApollo(createApolloClient);
Then you can use the hoc for your next page as such.
import React from 'react';
import { useQuery } from '#apollo/react-hooks';
import withApollo from 'hocs/withApollo';
const QUERY = gql`
query Profile {
profile {
name
displayname
}
}
`;
const ProfilePage = () => {
const { loading, error, data } = useQuery(PROFILE_QUERY);
if (loading) {
return <p>loading..</p>;
}
if (error) {
return JSON.stringify(error);
}
return (
<>
<p>user name: {data.profile.displayname}</p>
<p>name: {data.profile.name}</p>
</>
);
};
export default withApollo(ProfilePage);
I want to persist some parts of my state tree to the localStorage. What is the appropriate place to do so? Reducer or action?
Reducer is never an appropriate place to do this because reducers should be pure and have no side effects.
I would recommend just doing it in a subscriber:
store.subscribe(() => {
// persist your state
})
Before creating the store, read those persisted parts:
const persistedState = // ...
const store = createStore(reducer, persistedState)
If you use combineReducers() you’ll notice that reducers that haven’t received the state will “boot up” as normal using their default state argument value. This can be pretty handy.
It is advisable that you debounce your subscriber so you don’t write to localStorage too fast, or you’ll have performance problems.
Finally, you can create a middleware that encapsulates that as an alternative, but I’d start with a subscriber because it’s a simpler solution and does the job well.
To fill in the blanks of Dan Abramov's answer you could use store.subscribe() like this:
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
Before creating the store, check localStorage and parse any JSON under your key like this:
const persistedState = localStorage.getItem('reduxState')
? JSON.parse(localStorage.getItem('reduxState'))
: {}
You then pass this persistedState constant to your createStore method like this:
const store = createStore(
reducer,
persistedState,
/* any middleware... */
)
In a word: middleware.
Check out redux-persist. Or write your own.
[UPDATE 18 Dec 2016] Edited to remove mention of two similar projects now inactive or deprecated.
If anybody is having any problem with the above solutions, you can write your own to. Let me show you what I did. Ignore saga middleware things just focus on two things localStorageMiddleware and reHydrateStore method. the localStorageMiddleware pull all the redux state and puts it in local storage and rehydrateStore pull all the applicationState in local storage if present and puts it in redux 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;
Building on the excellent suggestions and short code excerpts provided in other answers (and Jam Creencia's Medium article), here's a complete solution!
We need a file containing 2 functions that save/load the state to/from local storage:
// 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;
}
};
Those functions are imported by store.js where we configure our store:
NOTE: You'll need to add one dependency: 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;
The store is imported into index.js so it can be passed into the Provider that wraps 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')
)
Note that absolute imports require this change to YourProjectFolder/jsconfig.json - this tells it where to look for files if it can't find them at first. Otherwise, you'll see complaints about attempting to import something from outside of src.
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
I cannot answer #Gardezi but an option based on his code could be:
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
)
);
the difference is that we are just saving some actions, you could event use a debounce function to save only the last interaction of your state
If you don't need to copy all redux store to localStorage you can use the specific store arguments:
store.subscribe(()=>{
window.localStorage.setItem('currency', store.getState().lang)
})
And set initial state argument value like:
const initialState = {
currency: window.localStorage.getItem('lang') ?? 'en',
}
In this case, you don't need to pass const persistedState to const store = createStore()
I'm a bit late but I implemented a persistent state according to the examples stated here. If you want to update the state only every X seconds, this approach may help you:
Define a wrapper function
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!")
}
}
Call a wrapper function in your subscriber
store.subscribe((state) =>
{
updateLocalStorage(store.getState())
});
In this example, the state is updated at most each 5 seconds, regardless how often an update is triggered.
I was looking badly for an entire example on how to persist state into a local storage using redux-toolkit-persist with no success until I came across #canProm response above to solve my issue.
This is what is working for me
//package.json
"reduxjs-toolkit-persist": "^7.0.1",
"lodash": "^4.17.21"
//localstorage.ts
import localStorage from 'reduxjs-toolkit-persist/es/storage';
export const saveState = (state: any) => {
try {
console.log(state);
const serializableState = JSON.stringify(state);
localStorage.setItem('globalState', serializableState);
} catch (err) {
console.log('Redux was not able to persist the state into the localstorage');
}
};
export const loadState = () => {
try {
const serializableState: string | any =
localStorage.getItem('globalState');
return serializableState !== null || serializableState === undefined ? JSON.parse(serializableState) : undefined;
} catch (error) {
return undefined;
}
};
//slices - actions
//reduxjs-toolkit-slices.ts
import { combineReducers, createSlice, PayloadAction } from '#reduxjs/toolkit';
import { UserModel } from '../model/usermodel';
import { GlobalState } from './type';
const deaultState: GlobalState = {
counter: 0,
isLoggedIn: false
};
const stateSlice = createSlice({
name: "state",
initialState: deaultState,
reducers: {
isLoggedIn: (state, action: PayloadAction<boolean>) => {
console.log('isLogged');
console.log(state.isLoggedIn);
console.log(action);
state.isLoggedIn = action.payload;
console.log(state.isLoggedIn);
},
setUserDetails: (state, action: PayloadAction<UserModel>) => {
console.log('setUserDetails');
console.log(state);
console.log(action);
//state.userContext.user = action.payload;
}
}
});
//export actions under slices
export const {
isLoggedIn: isUserLoggedAction,
setUserDetails: setUserDetailActions
} = stateSlice.actions;
//TODO: use the optimal way for combining reducer using const
//combine reducer from all slice
export const combineReducer = combineReducers({
stateReducer: stateSlice.reducer
});
//storeConfig
//reduxjs-toolkit-store.ts
import { configureStore } from '#reduxjs/toolkit';
import { throttle } from 'lodash';
import { persistReducer } from 'reduxjs-toolkit-persist';
import autoMergeLevel2 from 'reduxjs-toolkit-persist/lib/stateReconciler/autoMergeLevel2';
import storage from 'reduxjs-toolkit-persist/lib/storage';
import { loadState, saveState } from './localStorage';
import { combineReducer } from './reduxjs-toolkit-slices';
// persist config
const persistConfig = {
key: 'root',
storage: storage,
stateReconciler: autoMergeLevel2,
};
const persistedReducer = persistReducer(persistConfig, combineReducer);
// export reducers under slices
const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
preloadedState: loadState(), //call loadstate method to initiate store from localstorage
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
thunk: true,
serializableCheck: false,
}),
});
// handle state update event. Whenever the state will change, this subscriber will call the saveState methode to update and persist the state into the store
store.subscribe(throttle(() => {
saveState(store.getState());
}, 1000));
export default store;
//App.ts
import { persistStore } from 'reduxjs-toolkit-persist';
import { PersistGate } from 'reduxjs-toolkit-persist/integration/react';
import './i18n';
let persistor = persistStore(store);
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={<div>Loading .....</div>} persistor={persistor}>
<HalocarburesRouter />
</PersistGate>
</Provider>,
document.getElementById('ingenierieMdn'));