做网站的功能是什么,安徽网站设计方案,网上推广是什么意思,国内欣赏电商设计的网站redux和react-redux笔记#xff0c;以及项目中如何使用#xff0c;对redux的封装#xff0c;让其使用类似于vuex一样方便。
一、redux
1. redux工作流程 流程#xff1a;创建action dispatch分发action 交给store reducer加工数据返回给store
2. redux的…redux和react-redux笔记以及项目中如何使用对redux的封装让其使用类似于vuex一样方便。
一、redux
1. redux工作流程 流程创建action dispatch分发action 交给store reducer加工数据返回给store
2. redux的三个核心概念
2.1 action
动作对象包含两个属性
type标识属性值为string唯一必要属性data数据属性值类型任意可选属性
{ type: userInfo, data: { name: xiaotian, age: 20 } }2.2 reducer
用于初始化状态、加工状态。加工时根据旧的state和action产生新的state的纯函数。redux的reducer函数必须是一个纯函数。 纯函数同样的输入同样的输出 遵循 不得改写参数数据不会产生任何副作用例如网络请求输入输出设备不能调用Date.now()或者Math.random()等不纯的方法 例如下面这个函数就不是纯函数 // 传入同样的参数num每次返回的结果都不相同所以不是纯函数。
function fn(num) {return Math.random() num
}错误写法
let personList []
export default function personReducer(preState personList, action) {const { type, data } actionswitch (type) {case AddPerson:// 不能这样写会导致personList被改写了personReducer就不是一个纯函数了会导致reducx不能识别到数据的改变。personList.unshift(data)return personListdefault:return preState}
}正确写法
let personList []
export default function countReducer(preState personList, action) {const { type, data } actionswitch (type) {case AddPerson:return [data, ...personList]default:return preState}
}2.3 store
用于存储数据有如下方法 getState(): 获取当前的state对象。 dispatch(action): 分发action这是改变state的唯一途径。每个action是一个描述“发生了什么”的普通JavaScript对象。 subscribe(() {}): 注册一个监听器当state发生改变时会调用该回调。
3. redux使用
3.1 安装redux
npm install redux3.2 基本使用
新建redux目录在redux目录下新建store.js和xxx.jsstore.js
// 引入legacy_createStore专门用于创建redux中最核心的store对象
import { legacy_createStore } from redux
// 引入为xxx组件服务的reducer
import countReducer from ./count_reducer// 用于暴露一个store对象整个应用只有一个store
export default legacy_createStore(countReducer)xxx.js这里是count_reducer.js 创建一个为xxx组件服务的reducerreducer的本质就是一个函数 countReducer有两个参数preState之前的状态、action动作对象 reducer第一次调用时是store自动触发的传递的preState是undefinedaction是REDUX/INITxxx export default function countReducer(preState 0, action) {// 从action对象中获取type、dataconst { type, data } action// 根据type决定如何加工数据switch (type) {case increment:return preState data case decrement:return preState - datadefault:return preState}
}getState获取store中的state数据
store.getState()dispatchstore派发数据更新数据
store.dispatch({ type: increment, data: xxx })subscribe监听store中的数据变化
store.subscribe(() {// 调用render渲染页面
})组件中使用
componentDidMount() {store.subscribe(() {this.setState({})})
}全局index.js中使用
import store from ./redux/storeconst root ReactDOM.createRoot(document.getElementById(root))
root.render(React.StrictModeBrowserRouterApp //BrowserRouter/React.StrictMode
)store.subscribe(() {root.render(React.StrictModeBrowserRouterApp //BrowserRouter/React.StrictMode)
})3.3 异步action
action分类
同步action一般对象类型{type: string, data: any}异步action函数类型
异步action的使用
组件中
import React, { Component } from react
import store from ../redux/store
import { createIncrementAction, createAsyncIncrementAction } from ../redux/count_actionexport default class count extends Component {handleAsyncIncrement () {const { value } this.selectNumberstore.dispatch(createAsyncIncrementAction(value))}render() {return (divh1当前求和为: {store.getState()}/h1select ref{c this.selectNumber c}option value11/optionoption value22/optionoption value33/option/selectbutton onClick{this.handleAsyncIncrement}异步加/button/div)}
}count_action.js中(用于创建action对象)
// 异步action指action的值是函数异步action中都会调用同步action去真正的操作数据
export const createAsyncIncrementAction (data) {return (dispatch) {// 由redux调用传递参数dispatchsetTimeout(() {dispatch({ type: increment, data: data * 1 })}, 1000)}
}⚠️注意redux的action默认是不支持函数类型的需要使用中间件redux-thunk。 安装 npm install redux-thunkstore.js中
import { legacy_createStore, applyMiddleware } from redux
// 引入redux-thunk用于支持异步action
import { thunk } from redux-thunk
import countReducer from ./count_reducerexport default legacy_createStore(countReducer, applyMiddleware(thunk))这样就可以使用异步action了。
二、react-redux类式组件中使用 react-redux搭配redux使用步骤
1. 目录结构 components: 存放UI组件containers: 存放容器组件redux: 存放redux仓库
2. react-redux的基本使用
2.1 redux
store.js: 创建store
import { legacy_createStore, applyMiddleware } from redux
import count_reducer from ./count_reducer
import { thunk } from redux-thunkexport default legacy_createStore(count_reducer, applyMiddleware(thunk))count_reducer.js
export default function countReducer(preState 0, action) {const { type, data } actionswitch (type) {case ADD:return preState datadefault:return preState}
}2.2 containers容器组件: containers/count.jsx
容器组件的store是靠父组件的props传递进去的2.3的App.jsx)而不是自己import引入的
// 引入cont的ui组件
import CountUI from ../components/count
// 引入connect用于连接ui组件和redux
import { connect } from react-redux// 1. mapStateToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的keyvalue就作为传递给ui组件props的value
// 3. mapStateToProps函数的本质传递状态把状态传递给props
function mapStateToProps(state) {return {count: state}
}// 1. mapDispatchToProps函数的返回的是一个对象
// 2. 返回对象中的key就作为传递给ui组件的props的keyvalue就作为传递给ui组件props的value
// 3. mapDispatchToProps函数的本质传递操作状态的方法
function mapDispatchToProps(dispatch) {return {add: () {console.log(add)dispatch({ type: ADD, data: 1 })}}
}// 使用connect()()创建并暴露一CountUI的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI)2.3 App.jsx ⚠️注意这里引入的Count组件是容器组件(2.2 创建的)而不是UI组件 通过props的方式将store传入给容器组件在mapStateToProps中就可以获取到statemapDispatchToProps中可获取到dispatch
import React, { Component } from react
import Count from ./containers/count
import store from ./redux/storeexport default class App extends Component {render() {return divh1App/h1Count store{store} //div/}
}2.4 componentsUI组件: components/count.jsx
这个时候通过this.props即可获取redux的store仓库中数据和方法在2.2容器组件定义的数据和方法
import React, { Component } from reactexport default class count extends Component {handleAdd () {this.props.add()}render() {// console.log(ui组件props, this.props)return (divpnum为: {this.props.count}/pbutton onClick{this.handleAdd}1/button/div)}
}3. 优化
3.1 优化1容器组件mapDispatchToProps的简写
mapDispatchToProps可以是函数也可以是对象
value只要返回actionreact-redux自动dispatch分发action
// 引入cont的ui组件
import CountUI from ../components/count
// 引入connect用于连接ui组件和redux
import { connect } from react-reduxexport default connect(state ({count: state}),// mapDispatchToProps的简写{add: () ({ type: ADD, data: 1 })}
)(CountUI)3.2 优化2不用再手动监测redux数据的变化react-redux自动监测
3.3 优化3Provider使用
如果有多个容器组件需要每个容器组件都传入store就类似这样
Count store{store} /
Demo store{store} /
Text store{store} /优化可以通过Provider向每个容器组件都传入store在入口文件中如下使用
import React from react
import ReactDOM from react-dom/client
import App from ./App.jsx
import { Provider } from react-redux
import store from ./redux/store.jsReactDOM.createRoot(document.getElementById(root)).render(React.StrictModeProvider store{store}App //Provider/React.StrictMode,
)需要组件的地方直接使用即可
Count /3.4 整合UI组件容器组件
import React, { Component } from react
import { connect } from react-redux// ui组件
class count extends Component {handleAdd () {this.props.add()}render() {return (divpnum为: {this.props.count}/pbutton onClick{this.handleAdd}1/button/div)}
}// 容器组件
export default connect(state ({count: state}),{add: () ({ type: ADD, data: 1 })}
)(count)4. 多个reducer的使用
store.js中
import { legacy_createStore, applyMiddleware, combineReducers } from redux
import count_reducer from ./reducers/count
import person_redercer from ./reducers/person
import { thunk } from redux-thunk// 汇总所有的reducer变成一个总的reducer
const allReducer combineReducers({count: count_reducer,personList: person_redercer
})export default legacy_createStore(allReducer, applyMiddleware(thunk))容器组件中
import React, { Component } from react
import { connect } from react-reduxclass count extends Component {handleAdd () {// 操作状态的方法不需要区分this.props.add()}render() {return (divpnum为: {this.props.count}/pbutton onClick{this.handleAdd}1/buttondiv{this.props.personList.length}/div/div)}
}export default connect(state ({// state.xxxxxx是根据store.js中的allReducer中的key值决定的count: state.count,personList: state.personList}),{add: () ({ type: ADD, data: 1 })}
)(count)5. redux开发者工具的使用
5.1 下载浏览器插件
下载地址https://chrome.zzzmh.cn/info/lmhkpmbekcpmknklioeibfkpmmfibljd
5.2 项目中安装
npm i redux-devtools-extension5.3 在store.js中配置
import { legacy_createStore, applyMiddleware } from redux
import count_reducer from ./reducers/count
import { thunk } from redux-thunk
// 引入开发者工具
import { composeWithDevTools } from redux-devtools-extensionexport default legacy_createStore(count_reducer, composeWithDevTools(applyMiddleware(thunk)))这样浏览器插件就会亮了。
三、react-redux函数式组件使用
1. 可以通过UI组件和容器组件结合的写法
import { connect } from react-reduxconst CountUI (props) {return (divh1当前求和为{props.count}/h1button onClick{props.add}1/button/div)
}function mapStateToProps(state) {return {count: state}
}function mapDispatchToProps(dispatch) {return {add: () {console.log(add)dispatch({ type: ADD, data: 1 })}}
}export default connect(mapStateToProps, mapDispatchToProps)(CountUI)2. Hooks写法推荐
2.1 创建仓库
import { legacy_createStore } from redux
import reducer from ./reducer// window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__() 配置redux-devtools
const store legacy_createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__())
export default store2.2 创建reducer初始化仓库数据和修改仓库数据
// 仓库数据
const defaultState {num: 0
}// 当调用dispatch的时候会触发函数
let reducer (state defaultState, action) {// actiondispatch传递过来的对象// 对数据进行深拷贝let newState JSON.parse(JSON.stringify(state))switch (action.type) {case ADD1:newState.num 1break;case ADD2:newState.num - action.numbreak;default:break;}return newState
}export default reducer2.3 在main.js中将store与项目关联
import React from react
import ReactDOM from react-dom/client
import App from ./App.jsx
import { Provider } from react-redux
import store from ./store/index.jsReactDOM.createRoot(document.getElementById(root)).render(Provider store{store}App //Provider
)2.4 在组件中获取仓库数据和修改仓库数据2.2关联
获取仓库数据通过useSelector获取修改仓库数据通过useDispatch获取
import { useSelector, useDispatch } from react-reduxexport default () {// 通过useSelector获取仓库数据回调返回一个对象或数组const { num } useSelector((state) {return {num: state.num}})// 修改仓库数据const dispatch useDispatch()const changeNum () {// dispatch({type: 字符串, val: val可以自定义也可以不传})dispatch({type: ADD1})}const changeNum2 () {dispatch({type: ADD2, val: 2})}return (divp{num}/pbutton onClick{changeNum}num1/buttonbutton onClick{changeNum2}num2/button/div)
}3. 对react-redux的优化
3.1 将state数据抽离
新建index.js用于存放数据和方法
// index.js 用于存放数据
// src/store/index.js 用于存放num模块数据
export default {state: {num: 0},actions: {ADD1(newState, action) {newState.num 1},ADD2(newState, action) {newState.num action.val}},// 名字统一管理actionsName: {add1: ADD1,add2: ADD2}
}reducer.js
// 仓库数据
import handleStore from ./index
const defaultState {// num: handleStore.state.num 多个属性这么写会很麻烦...handleStore.state
}// 当调用dispatch的时候会触发函数
let reducer (state defaultState, action) {// actiondispatch传递过来的对象// 对数据进行深拷贝let newState JSON.parse(JSON.stringify(state))switch (action.type) {case handleStore.actionsName.add1:// handleStore.actions.ADD1(newState, action)// 将add1抽离handleStore.actions[handleStore.actionsName.add1](newState, action)break;case handleStore.actionsName.add2:handleStore.actions[handleStore.actionsName.add2](newState, action)break;default:break;}return newState
}export default reducer页面的使用参考2.4
3.2 仓库模块化
仓库目录结构 modules用于对模块进行划分 不同模块目录index.js用于存放仓库数据和方法reducer.js创建不同模块的reducer index.jsredux仓库
拆分优化过程
index.js进行reducer合并
import { legacy_createStore, combineReducers, } from redux
import num_reducer from ./modules/num/reducer
import arr_reducer from ./modules/arr/reducerconst reducers combineReducers({num_module: num_reducer,arr_module: arr_reducer
})const store legacy_createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ window.__REDUX_DEVTOOLS_EXTENSION__())
export default store页面上展示使用
import { useSelector, useDispatch } from react-reduxexport default () {const { num } useSelector((state) {return {// 需要加上模块名称使用num: state.num_module.num}})const dispatch useDispatch()const changeNum () {dispatch({ type: ADD1 })}const changeNum2 () {dispatch({ type: ADD2, val: 2 })}const { arr } useSelector((state) {return {// 需要加上模块名称使用arr: state.arr_module.arr}})const arrPush () {dispatch({ type: ARRPUSH, val: 40 })}return (divp{num}/pbutton onClick{changeNum}num1/buttonbutton onClick{changeNum2}num2/buttonhr /p{arr}/pbutton onClick{arrPush}push/button/div)
}对每个模块的reducer.js中的switch语句进行优化
// 仓库数据
import handleStore from ./index
const defaultState {// num: handleStore.state.num 多个属性这么写会很麻烦...handleStore.state
}// 当调用dispatch的时候会触发函数
let reducer (state defaultState, action) {// actiondispatch传递过来的对象let newState JSON.parse(JSON.stringify(state))// 优化前// switch (action.type) {// case handleStore.actionsName.add1:// // handleStore.actions.ADD1(newState, action)// // 将add1抽离// handleStore.actions[handleStore.actionsName.add1](newState, action)// break;// case handleStore.actionsName.add2:// handleStore.actions[handleStore.actionsName.add2](newState, action)// break;// default:// break;// }// 优化后for (const key in handleStore.actionsName) {if (action.type handleStore.actionsName[key]) {handleStore.actions[action.type](newState, action)break}}return newState
}export default reducer根据每个模块的index.js中的actions的名字自动生成actionsName也可以将actionsName进行提取到单独文件统管理
const store {state: {num: 0},actions: {ADD1(newState, action) {newState.num 1},ADD2(newState, action) {newState.num action.val}},// 名字统一管理最好将keyvalueactions的方法名// actionsName: {// ADD1: ADD1,// ADD2: ADD2// }
}let actionsName {}
for (const key in store.actions) {actionsName[key] key
}
Object.assign(store, { actionsName })export default store效果及其如何使用
效果做到了只有修改每个模块的index.js中的数据和方法不去动reducer.js文件使用起来跟vuex一样复制index.js和reducer.js在index.js的state中添加数据actions添加方法即可在store/index.js中引入不同模块的reducer
4. 异步action
4.1 跟之前的一样需要安装redux-thunk
4.2 在store/index.js中配置
import { legacy_createStore, combineReducers, compose, applyMiddleware } from redux
import { thunk } from redux-thunk
import num_reducer from ./modules/num/reducer
import arr_reducer from ./modules/arr/reducerconst reducers combineReducers({num_module: num_reducer,arr_module: arr_reducer
})// 判断是否有__REDUX_DEVTOOLS_EXTENSION__COMPOSE__这个开发者工具模块
let composeEnhancers window.__REDUX_DEVTOOLS_EXTENSION__COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION__COMPOSE__() : composeconst store legacy_createStore(reducers, composeEnhancers(applyMiddleware(thunk)))
export default store4.3 组件中使用
import { useSelector, useDispatch } from react-reduxexport default () {const { num } useSelector((state) {return {num: state.num_module.num}})const dispatch useDispatch()const asyncChangeNum () {// 异步用法dispatch中传入回调dispatch((disp) {setTimeout(() {disp({ type: add1, val: 1 })}, 1000)})}return (divp{num}/pbutton onClick{asyncChangeNum}num异步1/button/div)
}4.4 优化将组件内的方法抽取到每个模块的index.js中
index.js
const store {state: {num: 0},actions: {add1(newState, action) {newState.num action.val}},// 异步方法asyncActions: {asyncAdd1(dispathch) {setTimeout(() {dispathch({ type: add1, val: 1 })}, 1000)}}
}let actionsName {}
for (const key in store.actions) {actionsName[key] key
}
Object.assign(store, { actionsName })export default store组件内
import { useSelector, useDispatch } from react-reduxexport default () {const { num } useSelector((state) {return {num: state.num_module.num}})const dispatch useDispatch()const asyncChangeNum () {// 异步用法dispatch中传入回调dispatch(调用asyncAdd1方法即可)}return (divp{num}/pbutton onClick{asyncChangeNum}num异步1/button/div)
}