Dependency installation:
npm install redux react-redux
Create the src/store directory and create the createStore.js file
Create createStore.js file and export createStore method
import { combineReducers } from 'redux'
import userReducer from './user.reducer'
export default combineReducers(userReducer)
Create a reducers folder in the store directory to store reducers
src/store/reducers/user.reducer.js
export const InitialState = {
email: '[email protected]',
name: 'huiquan',
avatar: 'https://api.realworld.io/images/demo-avatar.png',
loginStatus: 0
}
export default (state = InitialState, action) => {
switch (action.type) {
case 'modify-email':
return { ...state, email: action.payload }
case 'modify-name':
return { ...state, name: action.payload }
case 'modify-login-status':
return { ...state, loginStatus: action.payload }
case 'login':
return { ...state, ...action.payload, loginStatus: 1 }
default:
return state
}
}
Create rootReducer.js
src/store/reducers/root.reducer.js Merge and export the reducers in the reducers directory through the combineReducers method
import { combineReducers } from 'redux'
import userReducer from './user.reducer'
export default combineReducers({ userReducer })
Configure the provider of the client, create a gatsby-browser.js file in the root directory, and export the wrapRootElement method
const React = require('react')
const Layout = require('./src/components/Layout').default
const { Provider } = require('react-redux')
const createStore = require('./src/store/createStore').default
module.exports = {
wrapPageElement: ({ element }) => {
return <Layout>{element}</Layout>
},
wrapRootElement: ({ element }) => {
return <Provider store={createStore()}>{element}</Provider>
}
}
Configure the provider on the server side and create a gatsby-ssr.js file in the root directory
const React = require('react')
const { Provider } = require('react-redux')
const createStore = require('./src/store/createStore').default
module.exports = {
wrapRootElement: ({ element }) => {
return <Provider store={createStore()}>{element}</Provider>
}
}
Use the data in the store and modify the data in the page or component
We introduce useDispatch, useSelector hook functions in the component, and use them to modify the state in the store:
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export default function Header () {
const userReducer = useSelector(state => state.userReducer)
const dispatch = useDispatch()
return (
<nav className='navbar navbar-light'>
<div className='container'>
<a className='navbar-brand' href='index.html'>
conduit
</a>
<ul className='nav navbar-nav pull-xs-right'>
<li className='nav-item'>
{/* Add "active" class when you're on that page" */}
<a className='nav-link active'>Home</a>
</li>
{userReducer.loginStatus === 1 && (
<li className='nav-item'>
<a className='nav-link'>
{' '}
<i className='ion-compose' />
New Article{' '}
</a>
</li>
)}
<li className='nav-item'>
<a className='nav-link'>
{' '}
<i className='ion-gear-a' />
Settings{' '}
</a>
</li>
{userReducer.loginStatus === 0 && (
<>
<li className='nav-item'>
<a
onClick={() => dispatch({ type: 'login' })}
className='nav-link'
>
Sign in
</a>
</li>
<li className='nav-item'>
<a className='nav-link'>Sign up</a>
</li>
</>
)}
{userReducer.loginStatus === 1 && (
<li className='nav-item'>
<a
className='nav-link ng-binding'
ui-sref-active='active'
ui-sref='app.profile.main({ username: $ctrl.currentUser.username })'
>
<img className='user-pic' src={userReducer.avatar} />
{userReducer.name}
</a>
</li>
)}
</ul>
</div>
</nav>
)
}
Add redux's asynchronous processing support, redux-saga
npm install redux-saga
Create the src/store/sagas folder, create src/store/sagas/user.saga.js and src/store/sagas/root.saga.js:
// src/store/sagas/user.saga.js
import axios from 'axios'
import { takeEvery, put, delay } from 'redux-saga/effects'
function* login_async (action) {
console.log(action.payload)
const { data } = yield axios.post(
'https://api.realworld.io/api/users/login',
{
user: action.payload.user
}
)
yield put({ type: 'login', payload: data.user })
}
export default function* userSaga () {
yield takeEvery('login_async', login_async)
}
// src/store/sagas/root.saga.js
import { all } from 'redux-saga/effects'
import userSaga from './user.saga'
export default function* rootSga () {
yield all([userSaga()])
}
In page components use:
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
export default function Header () {
const userReducer = useSelector(state => state.userReducer)
const dispatch = useDispatch()
return (
<nav className='navbar navbar-light'>
<div className='container'>
<a className='navbar-brand' href='index.html'>
conduit
</a>
<ul className='nav navbar-nav pull-xs-right'>
<li className='nav-item'>
{/* Add "active" class when you're on that page" */}
<a className='nav-link active'>Home</a>
</li>
{userReducer.loginStatus === 1 && (
<li className='nav-item'>
<a className='nav-link'>
{' '}
<i className='ion-compose' />
New Article{' '}
</a>
</li>
)}
<li className='nav-item'>
<a className='nav-link'>
{' '}
<i className='ion-gear-a' />
Settings{' '}
</a>
</li>
{userReducer.loginStatus === 0 && (
<>
<li className='nav-item'>
<a
onClick={() => dispatch({ type: 'login' })}
className='nav-link'
>
Sign in
</a>
</li>
<li className='nav-item'>
<a
onClick={() =>
dispatch({
type: 'login_async',
payload: {
user: {
email: '[email protected]',
password: '111111'
}
}
})
}
className='nav-link'
>
Sign up
</a>
</li>
</>
)}
{userReducer.loginStatus === 1 && (
<li className='nav-item'>
<a
className='nav-link ng-binding'
ui-sref-active='active'
ui-sref='app.profile.main({ username: $ctrl.currentUser.username })'
>
<img className='user-pic' src={userReducer.image} />
{userReducer.username}
</a>
</li>
)}
</ul>
</div>
</nav>
)
}