安装包
yarn add redux
来自 阮一峰
-
为了让代码各部分职责清晰、明确,Redux 提出三个核心概念,需要我们写代码的时候遵守。
a,action(动作):描述要做的事情(要干啥)。
b,reducer(函数):更新状态(怎么干)。
c,store(仓库):整合 action 和 reducer(谁来指挥)。
-
通过例子来理解三个核心概念。
a,action:相当于公司里面要做的事情,比如打扫卫生这个事等。
b,reducer:相当于公司的员工,负责执行。
c,store:相当于公司的老板。
d,流程:老板(store)分配(dispatch)要做的事情(action)给员工(reducer),员工干完活把结果交给老板。
e,在视图当中,通过 store dispatch 一个 action,reducer 会自动收到通知来更新 state,state 一旦变化,说有使用 state 的视图自然就变了。
Actions
用于定义方法
export const decrement =(payload)=>{
type:'DECREMENT',
payload
}
export const increment=(payload)=>{
type:'INCREMENT',
payload
}
Reducers
用于根据 Actions 里的方法来修改, 本质上是一个函数
// 用于处理action状态
// 参数:默认状态.action=>(type payload)
export default function counter (state = 10, action) {
// 根据action.type 的不同做不同的处理
switch (action.type) {
case 'DECREMENT':
return state - action.payload
case 'INCREMENT'
return state + action.payload
// 默认一般需要返回一个 state
default:
return state
}
}
-------------------------------------------
export default function counter (state = 10, action) {
if(action.type === 'DECREMENT'){
return state - action.payload
}
if(action.type === 'INCREMENT'){
return state + action.payload
}
return state
}
Reducers 是一个纯函数.
纯函数定义
相同输入就能得到相同的输出
a. 不得改写参数,不能使用全局变量
b. 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
c. 不包含副作用处理.,副作用:AJAX请求.操作本地数据.或者操作函数外部变量
好处:代码简洁.方便测试.方便性能优化.
Store
import { createStore } from 'redux'
import counterReducer from './reducers'
import { increment, decrement } from './actions'
// 参数:传递一个 reducer
// 返回值:store 实例
const store = createStore(counterReducer)
// 先订阅后续才能被监听到.一定要写在事件触发之前
// unsubscribe 用于停止监测
store.subscribe(() => {
// 一旦状态变化了.就会触发这里.
console.log(store.getState());
})
// 当前属性加5
store.dispatch(increment(5))
//当前属性减5
store.dispatch(decrement(3))
export default store
执行流程:
获取默认值的执行过程
1. 只要创建 store,Redux 内部就会调用一次 reducer,打印试一下 console.log(action.type)。2. 类似:reducer(undefined, {type: "@@redux/INITv.a.4.t.t.p"})。
3. 这一次调用 reducer 的目的:获取状态的默认值。
4. 因为传入的状态值是 undefined ,并且是一个随机的 action type,所以!
5. a,状态值因为 undefined,所以,我们设置的默认值就会生效,比如,此处的:10。
6. b,因为是一个随机的 action type,所以,reducer 中 switch 一定无法命中,那就一定会走 default,也就是直接返回了状态的默认值,也就是:10。
7. Redux 内部拿到这个数据(比如此处的 10)以后,就用这个数据作为了 store 中的最新状态值。
8. 因此,将来当我们调用 store.getState() 方法来获取 Redux 状态值的时候,拿到的就是 10 了。
React-Redux
为了将React Redux 两个结合在一起.使用
1. 导入包 import { Provider } from 'react-redux'
2. 包裹对应组件 并传递store ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root'))
3. 导入useSelector, useDispatch
4. 导入increment, decrement
5. 生成const state = useSelector(state=>state)
6. const dispatch = useDispatch()
7. 使用则是state
8. dispatch.increment(你需要的参数)
9. dispatch.decrement(你需要的参数)
10. state数据是公共的.哪里修改都会更改全部视图效果
Reducer 的分离与合并
1. 把 reducers 中的直接导出改为按需导出
2. 使用combineReducers接收.默认值为 counter user.
3. 定义user. 如果不是user 要修改.
4. 在需求的地方再导出使用.
---------------------------------------------------------
import { createStore, combineReducers } from 'redux'
import { counter, user } from './reducers'
const username = combineReducers({
counter,
user,
})
const store = createStore(username)
export default store
1. 如果使用大型项目则可以使用
单独封装一个js文件定义
export const TODO_DEL = 'TODO_DEL'
export const TODO_CHANG_STATUS = 'TODO_CHANG_STATUS'
export const TODO_ADD = 'TODO_ADD'
export const TODO_CHECKED = 'TODO_CHECKED'
---------------------------------------------
2. 在actions中定义
import { TODO_CHECKED, TODO_DEL, TODO_ADD, TODO_CHANG_STATUS } from "../constans"
export const tododel = (id) => ({
type: TODO_DEL,
id,
})
export const changStatus = (id) => ({
type: TODO_CHANG_STATUS,
id
})
export const todoAdd = (name) => ({
type: TODO_ADD,
name,
id: Date.now(),
done: false
})
export const todochecked = (done) => ({
type: TODO_CHECKED,
done
})
------------------------------------------
3. 在reducers中定义
import { TODO_CHECKED, TODO_DEL, TODO_CHANG_STATUS, TODO_ADD } from "../../constans"
const initState = [
{
id: 1,
name: '吃饭',
done: true,
},
{
id: 2,
name: '睡觉',
done: false,
},
]
export default function todo (state = initState, action) {
switch (action.type) {
case TODO_DEL:
if (action.type === TODO_DEL) {
return state.filter((item) => item.id !== action.id)
}
case TODO_CHANG_STATUS:
return state.map((item) => {
if (item.id === action.id) {
return {
...item,
done: !item.done
}
}
else {
return item
}
})
case TODO_ADD:
const { type, ...res } = action
return [
res,
...state
]
case TODO_CHECKED:
return state.map((item) => {
return {
...item,
done: action.done
}
})
default: state
}
return state
}
------------------------------------------------------
4.最后在需求的组件模块中使用方法
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { todoAdd } from '../actions'
export default function Header () {
const [name, setName] = useState('')
const dispatch = useDispatch()
const handlekey = (e) => {
if (e.keyCode === 13) {
if (name.trim().length === 0) return alert('格式有误')
dispatch(todoAdd(name))
setName('')
e.target.value = ''
}
}
return (
<>
<header className='header'>
<h1>todos</h1>
<input
className='new-todo'
placeholder='What needs to be done?'
autoFocus
onChange={(e) => setName(e.target.value)}
onKeyDown={handlekey}
/>
</header>
</>
)
}
定义顺序.
1. index.js 导入
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider >,
document.querySelector('#root'))
2. store/index.js 导入中间件和 reducer
import { createStore, applyMiddleware } from 'redux'
// 用于发起异步请求的包
import thunk from 'redux-thunk'
import reducer from './reducers'
export default createStore(reducer, applyMiddleware(thunk))
3. reducers/index.js 根据模块导入导出 在reducers中定义所对应的模块方法
// 用于合并导出
import { combineReducers } from 'redux'
import channel from './channel'
import news from './news'
export default combineReducers({
channel,
news,
})
4. constants(用于定义常量,可有可无,根据需求.) => actions => reducers 按这个顺序一次定义模块中需要修改的数据方法
最后在模块中使用
import { useDispatch, useSelector } from 'react-redux'
const list = useSelector((state)=> state )
const dispatch = useDispatch()
关于异步请求方法:
1.下包
yarn add redux-thunk
2.导入
import thunk from 'redux-thunk'
3.导出使用
export default createStore(reducer, applyMiddleware(thunk))
4.具体定义方式
import axios from 'axios'
export const getChannelListAc = (payload) => ({
type: CHANNEL_GET,
payload,
})
export const getChannelList = () => {
return async (dispatch) => {
const res = await axios.get('http://xxxx')
dispatch(getChannelListAc(res.data))
}
}
5.使用.获得得到数据
const dispatch = useDispatch()
useEffect(() => {
dispatch(getChannelList())
}, [dispatch])