一、如何安装Redux
npm install redux
二、Redux插件中的函数
1、applyMiddleware: [ Function: applyMiddleware ]
这个函数是用来做中间件的,用来做异步操作
2、bindActionCreators: [ Function: bindActionCreators ]
3、combineReducers: [ Function: combineReducers ]
这个函数是用来做redux的模块化的
4、compose: [ Function: compose ]
5、createStore: [ Function: createStore ]
这个函数是用来创建仓库实例的(新版中已不推荐用这个函数创建实例)
6、legacy_createStore: [ Function: createStore ]
这个函数是新版中推荐的仓库实例创建函数
三、如何创建Redux仓库实例
// 引入Redux组件
const redux = require("redux");
// 或者用import解构获取创建仓库方法
import {legacy_createStore} from "redux";
// 创建仓库实例有两个参数,其中第一个参数reducer为必选项
// reducer有两个参数:state,action
// state 仓库的数据,需要赋值一个初始数据
const defaultState = {
count:1,
name:"zhangsan",
}
// action 改变仓库数据的方法
// action为对象风格的提交方式,type属性表示方法名,amout表示参数
const reducer = (state=defaultState,action) => {
switch(action.type) {
case "add":
return {
...state,
count:state.count + 1
};
case "minus":
return {
...state,
count:state.count - 1,
};
default:
return state;
}
}
// 创建仓库实例
const store = redux.legacy_createStore(reducer);
四、Redux仓库实例中有哪些函数
1、dispatch: [ Function: dispatch ]
dispatch函数是用来触发reducer的,类似于vuex的mutation
store.dispatch()
2、subscribe: [ Function: subscribe ]
subscribe函数是用来监听state状态的
3、getState: [ Function: getState ]
getState函数是用来获取仓库数据的,类似于vuex的state
获取state中的状态的方法:store.getState().count
4、replaceReducer: [ Function: replaceReducer ]
5、@@observable: [ Function: observable ]
五、React结合Redux
1、如何让Redux的state更新具有响应式
第一种:这种方法也可以实现想响应式,但是每次渲染都会重新渲染整个页面,开销太大。
// index.js react的主入口文件
// 引入已经创建好的Redux仓库实例文件
import store from "./xxx"
// 在render函数下面追加一个监听Rudex的state状态的方法,subscribe(),在这个方法中加入一个回调函数并重新调用render方法
const render = () => {
root.render(<App />);
};
render();
store.subscribe(() => {
render();
})
第二种:引入react-redux插件
下载:npm install react-redux
// index.js react的主入口文件
// 在引入react和react-dom文件后
// 引入react-redux文件,这个文件相当于这两个框架的粘合剂
import {Provider} from "react-redux";
// 引入仓库实例
import store from "./xxx";
// 创建render函数时加入react-redux中的Provider方法
root.render(
<Provider store={store}>
<App />
<Provider>
);
// 这一步类似context的Provider,相当于在整个组件最外层包了一个数据总线,这样我们在这个实例中的所有子组件中都可以通过类似context的方法调用总线中的数据或方法了,当然,具体实现和方法名称可能与context会有所区别
// 如何在子组件中调用state和方法
// 引入rect-redux中的connect方法
import {connect} from "react-redux";
// connect的返回值是一个高阶函数,这个高阶函数接收一个组件,所以我们可以将需要接受总线数据或方法的组件放进该工厂中进行加工
// connect方法本身也会接收两个函数作为参数
// 第一个参数,将state作为映射到子组件的props中,返回值为一个对象
const mapStateToProps = (state) => {
return {
count:state.count,
}
}
// 第二个参数,将总线中的方法映射到子组件的props中
const mapDispatchToProps = (dispatch) => {
return {
add() {
dispatch({type:"increment"})
}
}
}
// 创建一个组件,省略
// 暴露时调用connect方法
export default connect(mapStateToProps,mapDispatchToProps)(App)
2、装饰器
先安装三个插件
npm install @babel/core @babel/plugin-proposal-decorators @babel/preset-env
创建 .babelrc
{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
创建config-overrides.js
const path = require('path')
const { override, addDecoratorsLegacy } = require('customize-cra')
function resolve(dir) {
return path.join(__dirname, dir)
}
const customize = () => (config, env) => {
config.resolve.alias['@'] = resolve('src')
if (env === 'production') {
config.externals = {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
return config
};
module.exports = override(addDecoratorsLegacy(), customize())
安装依赖
npm install customize-cra react-app-rewired
修改package.json
...
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
...
六、Redux的异步操作
1、我们需要安装一个异步库
npm install Redux-thunk
类似的异步库还有
Redux-saga
Redux-effects
Redux-side-effects
Redux-loop
Redux-observable
2、在创建仓库实例时,可以加入第二个参数,applyMiddleware(),第二个参数为applyMiddleware函数的返回值,参数为我们引入的异步库
// 解构引入Redux仓库创建函数和中间件函数
import {legacy_createStore,applyMiddleware} from "redux";
// reducer参数我们通常把它单独做一个文件用来放置修改state状态的函数和state状态本身
// actionCreator是我们放置异步操作的函数的文件,后面会讲,这里只是提一下
import reducer from "./reducer";
// 引入异步库
import thunk from "redux-thunk";
//创建仓库实例
const store = legacy_createStore(reducer,applyMiddleware(thunk));
export default store;
3、在actionCreator文件中的函数里,我们想要做异步操作,
// 这里是暴露一个调用方法,我们可以在组件中调用方法时,使用这个方法名,例如
add(){
dispatch({type:increment})
}
// 可以替换为
add(){
dispatch(addAction)
}
// 虽然这样的同步操作显得很麻烦,也没有必要,但是对于异步操作,这一步却很关键,因为在子组件中的dispatch只能接收一个扁平化的对象,而我们使用异步之后只能返回过去一个函数体,这样会直接报错,所以我们需要一个中间体来接收我们的函数,这个异步操作完成返回一个扁平化对象后再传递给子组件
export const addAction = () => {
return {type:"increment"}
}
// 异步操作时
export const addAction = () => {
return (dispatch) => {
setTimeout(() => {
dispatch({type:"increment"});
},2000);
};
};
// 我们可以在return时传入一个dispatch参数,在内部通过使用dispatch参数调用仓库中的方法,最后可以返回一个扁平化对象,这是中间件带来的操作,具体实现可以后面在看源码学习
七、Redux的hooks
我们可以从react-redux中解构出两个方法,一个useDispatch,一个useSelector,这两个hooks都只能在函数组件中使用,无法在类组件中使用。
useSelector
useSelector可以从仓库中获取state的状态,参数是一个回调函数,回调函数左边的state代表仓库中的state,有边可以返回state中的参数。
import {useSelector,useDispatch} from "react-redux";
// 函数组件最顶层
const state = useSelector(state=>state);
// 我们就可以在下面直接调用state中的状态了,例如div中
<div>{state.count}</div>
useDispatch
useDispatch可以调用仓库中的同步方法,使用时直接调用即可,没有参数,在组件中使用时用dispatch({ type:”xxx” })即可
const dispatch = useDispatch();
//在jsx的标签中直接调用
<button onClick={()=>dispatch({type:"add"})}>btn</button>
saga中间件
前面的步骤和thunk差不多,但是引入时使用有区别
1、首先我们需要创建一个saga.js的文件,代替之前的actionCreator.js,我们的异步操作要放在mysaga.js中使用。
在创建仓库实例的文件中
// 仓库实例的index.js
import {legacy_createStore, applyMiddleware} from "redux";
import createSagaMiddleware from "redux-saga";
import reducer from "./reducer";
import mySaga from "./mySaga";
const sagaMiddleware = createSagaMiddleware();
const store = legacy_createStore(reducer,applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);
export default store;
在saga.js文件中
// saga.js
// 我们可以看见,最下面的mySaga Generator函数是整合了上面所有方法的总体
// takeEvery()第一个参数是暴露出去给外面组件使用时的方法名,第二个参数表示调用的是saga,js中哪个方法
// put()方法表示触发reducer中的哪个同步方法
// call()方法就是用来请求数据的
import { takeEvery, put, call } from "redux-saga/effects";
function* addAction() {
yield put({ type: "add" });
}
function* loadAction() {
const res = yield call({ type: "add" });
yield put({type:"load",payload:res});
}
function* mySaga() {
yield takeEvery("addAction", addAction);
}
export default mySaga;