Redux 是一个独立的 JavaScript 状态管理库,不依赖于任何其他库
1. 安装
npm i redux
yarn add redux
2. Redux核心概念
1)state:状态
通常我们会把应用中的数据存储到一个对象树(Object Tree) 中进行统一管理,我们把这个对象树称为:state
state 是只读的,这里需要注意的是,为了保证数据状态的可维护和测试,不推荐直接修改 state 中的原数据
2)reducer:纯函数
什么是纯函数?
- 相同的输入永远返回相同的输出
- 不修改函数的输入值
- 不依赖外部环境状态,只依赖其参数
- 无任何副作用
使用纯函数的好处:
- 便于测试
- 有利重构
3)store:仓库
为了对 state,Reducer,action 进行统一管理和维护,我们需要创建一个 Store 对象
- getState: ƒ getState() // 获取 redux 存储的状态
- dispatch: ƒ dispatch(action) // 发起一个修改
调用 dispatch,store 会调用其绑定的 reducer 函数,并且会将当前的 state 和 action 传入 reducer。然后获取到 reducer 的返回值,指定为仓库的 state - replaceReducer: ƒ replaceReducer(nextReducer) // 替换掉当前的 reducer
- subscribe //监听状态发生改变
4)action:动作
action 是一个纯对象(普通对象),action 必须有一个 type 属性,用于描述我们对 state 做出何种修改
我们对 state 的修改是通过 reducer 纯函数来进行的,同时通过传入的 action 来执行具体的操作,action 是一个对象
- type 属性 : 表示要进行操作的动作类型,增删改查……
- payload属性 : 操作 state 的同时传入的数据
但是这里需要注意的是,我们不直接去调用 Reducer 函数,而是通过 Store 对象提供的 dispatch 方法来调用
3.redux三大原则
- 单一数据源: 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
- state 是只读的: 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
- 使用纯函数来执行修改
4.redux API
1)createStore(reducer, [preloadedState], enhancer);
- reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 action,返回新的 state 树。
- [preloadedState] (any): 初始时的 state。 在同构应用中,你可以决定是否把服务端传来的 state 后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 combineReducers 创建 - reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。
- enhancer (Function): Store enhancer 是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。这与 middleware 相似,它也允许你通过复合函数改变 store 接口。
- 返回值 (Store): 保存了应用所有 state 的对象。改变 state 的惟一方法是 dispatch action。你也可以 subscribe 监听 state 的变化,然后更新 UI。
import { createStore } from "redux";
const store = createStore(reducer);
2)reducer
- reducer(state,action)
function reducer(state = {
count: 1
}, action) {
//console.log(state,action);
switch (action.type) {
case "add":
return {
count: state.count + 1
}
case "minus":
return {
count: state.count - 1
}
}
return state;
}
3)Store
- getState()
- dispatch(action)
- subscribe(listener)
- replaceReducer(nextReducer)
let unSubscribe = store.subscribe(() => {
render();
});
render();
function render() {
ReactDOM.render(<div>
<p>{store.getState().count}</p>
<button
onClick={() => {
store.dispatch({
type: "add"
})
}}
>+</button>
<button
onClick={() => {
store.dispatch({
type: "minus"
})
}}
>-</button>
<button onClick={unSubscribe}>取消监听</button>
<button onClick={() => {
unSubscribe = store.subscribe(() => {
render();
});
}}>添加监听</button>
</div>,
document.querySelector("#root")
);
}
4)combineReducers(reducers)
将 reducer 函数拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分
import { createStore,combineReducers } from "redux";
import count from "./count";
import todo from "./todo";
const store = createStore(combineReducers({count,todo}));
export default store;
5)applyMiddleware(...middlewares) 中间件
中间件:更新的过程中,去做一些其他的事情
- dispatch ---> reducer 更新state
- dispatch --> 中间件 --> reducer
异步操作中间件:redux-thunk
- 参数是对象,直接调用 reducer 修改我们的 state
- 参数是函数,调用该函数,并且把 dispatch 和 getState 传递我们的函数,可以在函数中,进行异步操作
store/store.js
import { createStore,combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import count from "./count";
import todo from "./todo";
import topics from "./topics"
const store = createStore(combineReducers({count,todo,topics}),applyMiddleware(thunk));
export default store;
store/topics.js
import axios from "axios";
function topicsLoading(d,g) {
const {type} = g().topics;
return axios.get(`https://cnodejs.org/api/v1/topics?tab=${type}&page=1&limit=20`)
.then(res=>{
d({
type: "topics/load",
data:res.data.data
})
})
}
export {topicsLoading}
topics/topics.js
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { topicsLoading } from "../store/topics";
import List from "./list";
import Nav from "./navs";
function Topics() {
const {loading,type,data} = useSelector(state=>state.topics);
const d = useDispatch();
useEffect(()=>{
d(topicsLoading);
},[type]);
return <div style={{
textAlign:"center"
}}>
<Nav />
{loading?<h3>数据请求中……</h3>:<List data={data} />}
</div>
}
export default Topics;
5. react-redux
react项目中的 redux 绑定库
1)安装
npm i react-redux
2)<Provider store>
import ReactDOM from "react-dom";
import {Provider} from "react-redux";
import App from "./App";
import store from "./store/store";
ReactDOM.render(
<Provider
store={store}
>
<App />
</Provider>,
document.querySelector("#root")
);
3)connect()
在组件中获取 react-redux 传递的 store 中的数据和操作
connect(mapStateToProps:()=>{})
mapStateToProps:根据 store 中的 state,映射出 要传递给组件的 props
mapStateToProps 的返回值会传递给组件,mapStateToProps 的返回值必须是一个对象
import {connect} from "react-redux";
function Count(props) {
//console.log(props);
const {count,dispatch} = props;
return <>
<p>{count}</p>
<button onClick={()=>{
dispatch({
type: "add"
})
}}>+</button>
<button onClick={()=>{
dispatch({
type: "minus"
})
}}>-</button>
</>
}
export default connect(state=>state)(Count);
4)hooks
- useDispatch 获取 dispatch
- useStore 获取 store
- useSelector 获取 state
import {useDispatch, useSelector, useStore} from "react-redux";
function Count() {
const count = useSelector(state=>state.count);
const d = useDispatch();
console.log(useStore());
return <>
<p>{count}</p>
<button onClick={()=>{
d({
type: "add"
})
}}>+</button>
<button onClick={()=>{
d({
type: "minus"
})
}}>-</button>
</>
}
export default Count;