目录:
1 Redux的核心思想
2 Redux的基本使用
3 React结合Redux
4 Redux的异步操作
5 redux-devtool
6 reducer的模块拆分
一、Redux的核心思想
举个例子:
下图是纯函数,每次输入都有一个确定的输出,不会被外界的变量影响
这个就不是纯函数,因为函数里面的count可以被改变,函数确定的输入却拿不到确定的输出了,输出被外界的变量影响
副作用就是函数执行过程有修改外界变量的操作:函数可以拿变量去用,不能修改外界变量
流程是dispatch派发事件给reducer,然后reducer return新的state给store(由于reducer是纯函数,所以不能直接通过state.xxx = action.xxx 来修改state,需要return来拷贝state,然后修改拷贝的state,最后替换原有的state)
第一个是纯函数,第二、三个不是纯函数
二、Redux的基本使用
常规的redux配置就是这4个文件,后续会举例redux现在版本的使用方法:
在node里面直接使用redux,首先创建store文件夹,下面分别创建4个文件,第一个是创建store的文件;第二个是存储字符串的文件,第三个是存储action的文件,第四个是reducer的文件。
在终端输入代码:
//用于创建package.json
yarn init -y
//下载redux的库,相当于在项目里面install
yarn add redux
第一个是创建store的文件index.js:
const { createStore } = require("redux")
const reducer = require("./reducer.js")
// 创建的store
//将reducer和默认值(initialState)放到一个独立的reducer.js文件中, 而不是在index.js 避免index.js太占内存
const store = createStore(reducer)
module.exports = store
第二个是创建字符串常量的文件constants.js:
//由于存储常量的文件
//actionCreators和reducer函数中使用字符串常量是一致的, 所以将常量抽取到一个独立constants的文件中
const ADD_NUMBER = "add_number"
const CHANGE_NAME = "change_name"
module.exports = {
ADD_NUMBER,
CHANGE_NAME
}
第三个是创建修改store数据的函数、方法的文件actionCreator.js:
//将派发的action生成过程放到一个actionCreators函数中
//由于存放action事件的文件,将定义的所有actionCreators的函数, 放到一个独立的文件中: actionCreators.js
const { ADD_NUMBER, CHANGE_NAME } = require("./constants")
//将派发的action生成过程放到一个actionCreators函数中
//actionCreators里面的changeNameAction方法运用了属性增强写法
//由于箭头函数没有逻辑代码,只有return内容的话,是可以不用写return,直接写要返回的内容
//这里相当于直接返回一个对象数据,所以不用写return
const changeNameAction = (name) => ({
type: CHANGE_NAME,
name
})
const addNumberAction = (num) => ({
type: ADD_NUMBER,
num
})
module.exports = {
changeNameAction,
addNumberAction
}
第四个是创建存储store的数据,reducer的文件reducer.js:
const { ADD_NUMBER, CHANGE_NAME } = require("./constants")
// 初始化的数据
const initialState = {
name: "why",
counter: 100
}
// 定义reducer函数: 纯函数
// 两个参数:
// 参数一: store中目前保存的state
// 参数二: 本次需要更新的action(dispatch传入的action)
// 返回值: 它的返回值会作为store之后存储的state
function reducer(state = initialState, action) {
//用switch的性能比if else好
switch(action.type) {
case CHANGE_NAME:
//这里用到return是因为我们不能直接修改initialState里面的数据,那样子没
//办法做到数据响应式,所以需要先拷贝一份原数据修改数据,再替换掉原来的数据
//直接修改数据是不对的,直接修改数据的代码一般都是这样子:state.xxx = xxx
return { ...state, name: action.name }
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num }
default:
// 没有新数据更新, 那么返回之前的state
return state
}
}
module.exports = reducer
然后就是如何使用redux存储的数据(在需要的js文件中):
//这里简写了,我们获取的是store底下的index.js文件
//这个文件就是正在使用redux里面的数据
const store = require("./store")
const { changeNameAction } = require("./store/actionCreators")
//后面这是没有actionCreators时候的写法store.dispatch({ type: "change_name", name: "kobe" })
//actionCreators里面的changeNameAction方法运用了属性增强写法
//每次调用dispatch都会重新执行reducer.js里面的reducer函数
store.dispatch(changeNameAction("curry"))
//这个方法可以获取所有变量
console.log(store.getState())
//这个方法可以在每次有任何变量发生改变的时候打印一次所有变量
const unsubscribe = store.subscribe(() => {
console.log("订阅数据的变化:", store.getState())
})
//取消订阅的方法
unsubscribe()
node中运行代码(列举的代码直接在node中运行):
node xxx.js
这个是在react的组件中使用redux相同的操作,通过store.getSate().counyer获取store里的counter的值,subscribe来订阅一旦有store的值发生变化,dispatch来执行store变量的改变。(不推荐使用)
上述组件使用redux很繁琐,可以通过高阶函数来优化:(这个方法对于类组件重要,后续有hooks的时候写法会改变)
1、通过社区下载这个高阶组件
npm install react-redux
2、在APP的index.jsx里面引入react-redux的Provider包裹app
3、引入store给Provide来提供数据
4、组件中使用store的数据需要引入高阶组件connect,在组件输出的时候用connect来包裹,而connect高阶组件第一个括号放的是你这个组件因为哪个store的值发生变化时才使组件发生更新,否则不更新,第二个括号放这个组件。而使用store的值需要通过
const { xxx } = this.props来取出需要的值。connect有两个括号是因为,第一个相当于函数的参数,第二个函数是connect的输出是一个函数,也需要参数。
5、组件间引入了store的数据之后需要调用store的修改state的方法,这时用到解耦
6、前面提到的修改store的state变量值的方法是同步方法,下面讲解如何使用异步方法从服务器请求数据并共享到所有其他组件
在一个组件里面专门发送axios请求
上面异步操作放到一个组件中是不合理的,请求数据的过程没法控制,又由于数据最后放到的是store的state里面,从其他组件请求数据放到store里面没意义,于是我们需要想办法在store里面放异步请求数据
(1)需要使用异步请求的组件代码
(2)在store的actionCreators。js里面需要让 输出函数,而 只能return对象,所以需要下载中间件redux-thunk
npm install redux-thunk
在store的index.js里面引入thunk使用E:\webstudy\reactstudy\reduxone\src\store\index.js
如果要使用多个中间件可以这样做
(3)然后就是在actionCreators.js里的代码
两个调试工具的使用:
1、下载:
(1)在Chrome的插件商店下载react-devtool和redux-devtool
(2)从GitHub上查找react-devtool和redux-devtool,但是版本很低,然后在左上角加载以解压的扩展程序打开就有了
2、使用:
component和profiler以及redux都是刚刚下载好的插件
component可以显示代码结构
redux工具: 由于这个是在开发阶段使用的调试工具,在生产阶段一般都是关闭了的,所以正常情况是看到redux里面的内容,如何打开这个工具?
在store文件夹下的index.js里面写入代码,同时还需要使用增强函数componentEnhancers来包裹applyMiddleware(),compose需要从redux里面引入
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
在生产环境里面需要删掉 window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__就可以关掉redux-devtool,就看到redux里面的内容了。
const composeEnhancers = compose;
redux工具里面的action可以看见所有的方法、函数
state可以看见所有变量:
diff可以看见数据是从什么变成那个数据
test可以编写测试代码:
trace可以帮助我们跟踪代码是在哪里执行的(默认是关的):
打开方式就是在里面加代码:
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace:true}) || compose;
三、React结合Redux
四、Redux的异步操作
五、redux-devtool
六、reducer的模块拆分
和之前的状态管理器是一样的,会有module这样的存储不同文件的数据和函数,在这里我们要在store文件夹内创建以组件名称为文件名的module:
store底下创建一个index.js用于把上图中的counter和home进行合并reducer,使用redux里的combineReducers来合并多个模块的reducer
由于是多个模块合并起来的,所以在很多组件里面引入某个数据的时候需要加模块名字
action的方法也需要从模块重新调用
下图 相当于 上图的合并