I have an app using Redux. Is stores the global state as shown below:
Create Store:
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../reducers';
import thunk from 'redux-thunk';
const configureStore = initialState => {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
};
export default configureStore;
Handles Local Storage
const storageName = 'xxxState';
export const loadState = () => {
try {
const serializedState = localStorage.getItem(storageName);
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(storageName, serializedState);
} catch(err) {
throw err;
}
};
Finaly starts the app session:
import React from 'react';
import ReactDOM from 'react-dom';
import Start from './start';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import { loadState, saveState } from './store/localStorage';
import throttle from 'lodash/throttle';
const persistedState = loadState();
const store = configureStore(persistedState);
store.subscribe(throttle(() => {
saveState(store.getState());
}, 1000));
ReactDOM.render(
<Provider store={store}>
<Start />
</Provider>,
document.getElementById('root'));
It works perfectly. So my question is: Is there any possibility to do the same with the new React Context API?
I am pretty confortable using the new context, I just want to know about store, localstorage, subscribe. So the the state is persisted even when the user leaves the app.
Sometimes Redux is overkill for simple applications, even with Redux Toolkit. Context, on the other hand, is not a replacement for Redux.
It's probably OK to use localstorage instead of Redux. It's possible to have changes in localstorage have immediate effect on subscribed react components. The people at marmelab who built react-admin transitioned from using redux to what they call The Store.
Saving data to localStorage in React is super easy: const [data, setData] = useState([]); useEffect(() => { localStorage. setItem('dataKey', JSON. stringify(data)); }, [data]);
store.js
import React, { Component, createContext } from "react";
export const createStore = store => {
const Context = createContext();
let self;
const setState = (action, state, args) => {
self.setState(state);
};
const actions = Object.keys(store.actions).reduce(
(accumulator, currentValue) => {
return {
...accumulator,
[currentValue]: function(...args) {
let result = store.actions[currentValue](
{ state: self.state, actions: self.value.actions },
...args
);
if (result) {
result.then
? result.then(result => setState(currentValue, result, args))
: setState(currentValue, result, args);
}
}
};
},
{}
);
class Provider extends Component {
constructor() {
super();
this.state = store.initialState;
self = this;
this.value = {
actions,
state: this.state
};
}
render() {
if (this.state !== this.value.state) {
this.value = {
actions,
state: this.state
};
}
return (
<Context.Provider value={this.value}>
{this.props.children}
</Context.Provider>
);
}
}
class Consumer extends Component {
renderProps = ({ actions, state }) => {
// const { mapStateToProps, mapDispatchToProps } = this.props;
return this.props.children({
state: state,
actions: actions
});
};
render() {
return <Context.Consumer>{this.renderProps}</Context.Consumer>;
}
}
const connect = (mapStateToProps, mapDispatchToProps) => WrappedComponent => {
return (
<Consumer
mapStateToProps={mapStateToProps}
mapDispatchToProps={mapDispatchToProps}
>
{injectedProps => (
<WrappedComponent {...injectedProps} {...this.props} />
)}
</Consumer>
);
};
return {
Provider,
Consumer,
connect
};
};
Then for individual pages you can have actions and initial store value like this
import { createStore } from "@src/store";
import { Actions } from "../actions";
import { storage } from "@helpers/storage";
import constants from "@src/constants";
import { util } from "@src/utils";
function getInitialState() {
let Id = null;
if (storage.get(constants.STORAGE_KEY)) {
let storageObject = storage.get(constants.STORAGE_KEY);
Id = storageObject["Id"];
selectedDate = storageObject["selectedDate"];
}
return {
data: null,
activeTab: "score",
Id: Id
};
}
const store = {
initialState: getInitialState(),
actions: Actions
};
export const { Provider, Consumer } = createStore(store);
Finally in your pages/HomePage/index.js you can have import { Provider } from './store';
render() {
return (
<Provider>
<Homepage user={user} children={children} />
</Provider>
);
}
and in your pages/HomePage/Homepage.js you can have
render() {
return (
<Consumer>
{({ state, actions }) => {
return this.renderChildrens(state, actions);
}}
</Consumer>
);
}
}
First things first
Redux has nothing to do with react
in specific, it is a state manager.
it helps you manage a big JavaScript object that holds the state of your application.
react's Context API is not even a new feature in react, it was there the whole time and it just got a new face. It's a way to pass down data to children without prop drilling.
I think you are referring to react-redux
.react-redux
is an abstraction, a way to bind your Redux store to react.
It's doing all the store subscription for you and help you create a Container
and consumers using the connect
HOC and the Provider
component.
It also helps with passing down the store object (via the context API under the hood).
If you are using Redux
already, i don't see any point for not using react-redux
.
You can read in more details about the differences In this post (full disclosure, I'm the author).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With