Example Idea The idea behind the Redux is to keep our states outside of components in one object. Every component has an access to the Redux and we do not have to pass states via props throughout the app, which can make the code messy. Info redux webpage tutorial from Vishvas Dev Ed simple example of Redux Install First of all install Redux with npm i redux & connect it to the React with npm i react-redux Redux basics In redux we deal with STORE , ACTION , REDUCER , DISPATCH STORE keeps all states ACTION is an object which describes what we want to do with a state DISPATCH sends an ACTION to a REDUCER REDUCER updates the STORE in accordance to an ACTION we choose createStore In the main app component we initiate the state store with createStore(rootReducer) with built-in function. STORE holds all states Provides dispatch(action) method to update the state Provides getState().state method to retrieve the state Provides subscribe(listener) register a listener, which is triggered every time the state changes Reducer REDUCER is a function where we set an initial state and return new one And tell how we want to modify it Modification will be done depending on an action.type inside dispatch({ type: 'SIGN_IN' }) function We should not mutate the state object, but return a new one Each reducer's state corresponds only the part of the global state
const initCounterState = 0
const counter = (state = initCounterState, action) => {
if (action.type === 'INCREMENT') return state + (action.num || 1)
if (action.type === 'DECREMENT') return state - action.num
return state
}
const initIsLoggedState = false
const isLogged = (state = initIsLoggedState, action) => {
if (action.type === 'SIGN_IN') return !state
return state
}
combineReducers Reducers can be combined into one object with combineReducers() function from the redux library.
const rootReducer = combineReducers({
counter,
isLogged
})
Dispatch To update a state we launch the dispatch(actionObj) function actionObj parameter { type: 'SIGN_IN' } corresponds to the action.type of a reducer dispatch works synchronously
store.dispatch({ type: 'INCREMENT' }) // 1
store.dispatch({ type: 'INCREMENT', num: 5 }) // 6
store.dispatch({ type: 'DECREMENT' }) // 5
Action ACTION is an object with type property { type: 'INCREMENT', payload: num } which describes how we want to modify the state and used in dispatch({ type: 'INCREMENT', payload: num }) function Action creator To avoid typing ACTION object by hand, we may use action creator function, which forms and returns an action object
const increment = (num = 1) => ({ type: 'INCREMENT', payload: num })
const decrement = (num = 1) => ({ type: 'DECREMENT', payload: num })
const signIn = () => ({ type: 'SIGN_IN' })
store.dispatch(increment())
store.dispatch(decrement(5))
store.dispatch(signIn())
getState With store.getState().property we may read property directly from the store object. subscribe Provides subscribe(listener) register a listener, which is triggered every time the state changes Understood that it needed for JS projects to react to the state change, because in React the React itself reacts to a state change subscribe() returns a function, which we may call to unsubscribe the listener
const unsubscribe = store.subscribe(() => console.log(store.getState()))
// ... and later
unsubscribe()
useSelector To extract a state from the Redux store can be done with built-in function useSelector() .
const counter = useSelector(state => state.counter)
Selector Function state => state.counter inside useSelector() returns part of the state It is called selector It can be extracted In case we use same selector the app several times and store structure changes all we need is only to modify selector function in one place
const getCounter = state => state.counter
const counter = useSelector(getCounter)
useDispatch useDispatch() hook returns a reference to the dispatch function from the Redux store. Provider The whole application should be wrapped in a <Provider> component to make the store available throughout the component tree. Middleware Extends Redux with custom functionality Provides extension point between dispatching an action and point it reaches the reducer May be used for logging, crash reporting, performing asynchronous tasks etc Whole code
import { Code, H, Lnk, jsxToStr, LazyImg } from '/components/post/reExport'
import { combineReducers, createStore, applyMiddleware } from 'redux'
import { Provider, useSelector, useDispatch } from 'react-redux'
import { createLogger } from 'redux-logger'
import { composeWithDevTools } from 'redux-devtools-extension'
// #region REDUCERS (sets and changes state)
const initCounterState = 0
const counter = (state = initCounterState, action) => {
if (action.type === 'INCREMENT') return state + (action.num || 1)
if (action.type === 'DECREMENT') return state - action.num
return state
}
const initIsLoggedState = false
const isLogged = (state = initIsLoggedState, action) => {
if (action.type === 'SIGN_IN') return !state
return state
}
const rootReducer = combineReducers({
counter,
isLogged
})
// #endregion
// #region ACTION CREATORS
const increment = (params) => ({ type: 'INCREMENT' })
const decrement = (params) => ({ type: 'DECREMENT', num: 5 })
const signIn = (params) => ({ type: 'SIGN_IN' })
// #endregion
// #region LOGGER MIDDLEWARE
const loggerMiddleware = createLogger({})
// #endregion
// #region STORE (holds all states)
const middlewares = [loggerMiddleware]
const store = createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(...middlewares)
)
)
// #endregion
// #region SUBSCRIBE - EVENT listener, called any time an action is dispatched and state changed
store.subscribe(() => console.log(store.getState())) // display in the console
// #endregion
// #region COMPONENT with useSelector() & useDispatch()
const style = { border: '2px solid grey', padding: '10px', margin: '10px', maxWidth: '500px' }
function Component() {
const counter = useSelector(state => state.counter)
const isLogged = useSelector(state => state.isLogged)
const dispatch = useDispatch()
return (
<div style={style}>
<div>Counter: <strong>{counter}</strong></div>
<button onClick={() => dispatch(increment())}>Increment +1</button> 
<button onClick={() => { dispatch(decrement()) }} > Decrement -5 </button>
<div>isLogged: <strong>{isLogged.toString()}</strong></div>
<button onClick={() => dispatch(signIn())}>Sign in/out</button><br />
<button onClick={() => { alert(store.getState().counter) }}>Get counter value from store</button>
</div>
)
}
// #endregion
Redux devtools extension It is worth to install Redux DevTools Extension for Chrome to observe state in real time in dev tools.