依赖安装:
npm install redux react-redux
创建src/store目录,创建createStore.js文件
创建createStore.js文件并导出createStore方法
import { combineReducers } from 'redux'
import userReducer from './user.reducer'
export default combineReducers(userReducer)
store 目录下创建 reducers文件夹,用于存放reducer
src/store/reducers/user.reducer.js
export const InitialState = {
email: 'denghuiquan@qq.com',
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
}
}
创建rootReducer.js
src/store/reducers/root.reducer.js 通过combineReducers方法对reducers目录下的reducers进行合并并导出
import { combineReducers } from 'redux'
import userReducer from './user.reducer'
export default combineReducers({ userReducer })
配置客户端的provider,在根目录下创建gatsby-browser.js文件,导出wrapRootElement方法
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>
}
}
配置server端的provider,在根目录下创建gatsby-ssr.js文件
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>
}
}
在页面或组件中使用store中的数据和修改数据
我们通过在组件中引入useDispatch, useSelector 钩子函数,并使用它们对store中的state进行修改:
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>
)
}
添加redux的异步处理支持,redux-saga
npm install redux-saga
创建src/store/sagas文件夹,创建src/store/sagas/user.saga.js和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()])
}
在页面组件中使用:
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: 'dhq@qq.com',
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>
)
}