目录一、理解javascript纯函数1.1 纯函数的概念1.2 副作用概念的理解1.3 纯函数在函数式编程的重要性二、Redux的核心思想2.1 为什么需要 Redux2.2 Re
纯函数的维基百科定义:
纯函数概念,总结如下:
案例(数组的两个方法):
什么是副作用?
纯函数在执行的过程中就是不能产生这样的副作用:
为什么纯函数在函数式编程中非常重要呢?
props
不被修改JavaScript开发的应用程序变得越来越复杂:
管理不断变化的state是非常困难的:
React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:
可以定义一些初始化的数据,通过 reducer 传入
将传入的state和action结合起来生成一个新的state
建议看完Redux基本使用后再来看这幅图:
注意:以下 3 部分代码在 node 环境下
npm install redux
node v13.2.0
开始,对ES6模块化提供了支持:
node v13.2.0之前,需要进行如下操作:
node v13.2.0之后,只需要进行如下操作:
.js
后缀名定义reducer:必须是一个纯函数,不要直接修改state
createStore 传入 reducer
const { createStore } = require('redux')
// 初始化的数据
const initialState = {
name: '李雷',
counter: 100
}
// 定义reducer函数:纯函数
// 两个参数:
// 参数一:store中目前保存的state
// 参数二:本次需要更新的action(dispatch传入的action)
// 返回值:返回值会作为store之后存储的state
function reducer(state = initialState, action) {
switch (action.type) {
case 'change_name':
return { ...state, name: action.name }
case 'add_numer':
return { ...state, counter: state.counter + action.num }
default:
return state
}
}
// 创建store
const store = createStore(reducer)
module.exports = store
const store = require('./store')
console.log(store.getState()) // { name: '李雷', counter: 100 }
// 修改store中的数据:必须action
const nameAction = { type: 'change_name', name: '韩梅梅' }
store.dispatch(nameAction)
console.log(store.getState()) // { name: '韩梅梅', counter: 100 }
const nameAction2 = { type: 'change_name', name: '夏洛' }
store.dispatch(nameAction2)
console.log(store.getState()) // { name: '夏洛', counter: 100 }
// 修改counter
const counterAction = { type: 'add_numer', num: 10 }
store.dispatch(counterAction)
console.log(store.getState()) // { name: '夏洛', counter: 110 }
store.subscribe()
传入一个函数能够监听数据的变化store.subscribe()
会返回一个函数,执行该函数取消监听const store = require('./store')
const unSubscribe = store.subscribe(() => {
console.log('订阅数据的变化:', store.getState())
})
// 修改store中的数据:必须action
store.dispatch({ type: 'change_name', name: '韩梅梅' })
store.dispatch({ type: 'change_name', name: '夏洛' })
// 取消订阅
unSubscribe()
// 修改counter
store.dispatch({ type: 'add_numer', num: 10 })
示例:
actionCreators.js
const { ADD_NUMBER, CHANGE_NAME } = require("./constants")
const changeNameAction = (name) => ({
type: CHANGE_NAME,
name
})
const addNumberAction = (num) => ({
type: ADD_NUMBER,
num
})
module.exports = {
changeNameAction,
addNumberAction
}
const ADD_NUMBER = "add_number"
const CHANGE_NAME = "change_name"
module.exports = {
ADD_NUMBER,
CHANGE_NAME
}
const { CHANGE_NAME, ADD_NUMBER } = require('./constants')
// 初始化的数据
const initialState = {
name: '李雷',
counter: 100
}
function reducer(state = initialState, action) {
switch (action.type) {
case CHANGE_NAME:
return { ...state, name: action.name }
case ADD_NUMBER:
return { ...state, counter: state.counter + action.num }
default:
return state
}
}
module.exports = reducer
const { createStore } = require('redux')
const reducer = require('./reducer')
// 创建store
const store = createStore(reducer)
module.exports = store
const store = require('./store')
const { changeNameAction, addNumberAction } = require('./store/actionCreators')
store.dispatch(changeNameAction('独孤月'))
store.dispatch(addNumberAction(100))
console.log(store.getState()) // { name: '独孤月', counter: 200 }
有两个组件,组件上展示同一个counter,并且两者能够对counter进行操作
actionCreators.js
import * as actionTypes from './constants'
export const addNumberAction = (num) => ({
type: actionTypes.ADD_NUMBER,
num
})
export const subNumberAction = (num) => ({
type: actionTypes.SUB_NUMBER,
num
})
constants.js
export const ADD_NUMBER = "add_number"
export const SUB_NUMBER = "sub_number"
reducer.js
import * as actionTypes from './constants'
const initialState = {
counter: 100
}
function reducer(state = initialState, action) {
switch (action.type) {
case actionTypes.ADD_NUMBER:
return { ...state, counter: state.counter + action.num }
case actionTypes.SUB_NUMBER:
return { ...state, counter: state.counter - action.num }
default:
return state
}
}
export default reducer
index.js
import { createStore } from "redux"
import reducer from "./reducer"
const store = createStore(reducer)
export default store
组件中使用:
import React, { PureComponent } from 'react'
// 引入store
import store from '../store'
import { addNumberAction } from '../store/actionCreators'
export default class Home extends PureComponent {
constructor() {
super()
this.state = {
counter: store.getState().counter
}
}
componentDidMount() {
store.subscribe(() => {
const state = store.getState()
this.setState({ counter: state.counter })
})
}
addNumber(num) {
store.dispatch(addNumberAction(num))
}
render() {
const { counter } = this.state
return (
<div>
<h2>Home Counter: {counter}</h2>
<div>
<button onClick={e => this.addNumber(1)}>+1</button>
<button onClick={e => this.addNumber(5)}>+5</button>
<button onClick={e => this.addNumber(8)}>+8</button>
</div>
</div>
)
}
}
import React, { PureComponent } from 'react'
// 引入store
import store from '../store'
import { subNumberAction } from '../store/actionCreators'
export default class Profile extends PureComponent {
constructor() {
super()
this.state = {
counter: store.getState().counter
}
}
componentDidMount() {
store.subscribe(() => {
const state = store.getState()
this.setState({ counter: state.counter })
})
}
subNumber(num) {
store.dispatch(subNumberAction(num))
}
render() {
const { counter } = this.state
return (
<div>
<h2>Profile Counter: {counter}</h2>
<div>
<button onClick={e => this.subNumber(1)}>-1</button>
<button onClick={e => this.subNumber(5)}>-5</button>
<button onClick={e => this.subNumber(8)}>-8</button>
</div>
</div>
)
}
}
安装:npm install react-redux
在使用时在入口文件中导入 Provider
,传入 store
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import store from './store'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
在 About 组件中使用:
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumberAction, subNumberAction } from '../store/actionCreators'
export class About extends PureComponent {
clacNumber(num, isAdd) {
if(isAdd) {
this.props.addNumber(num)
} else {
this.props.subNumber(num)
}
}
render() {
const { counter } = this.props
return (
<div>
<h2>About Counter: {counter}</h2>
<button onClick={e => this.clacNumber(6, true)}>+6</button>
<button onClick={e => this.clacNumber(9, true)}>+9</button>
<button onClick={e => this.clacNumber(6, false)}>-6</button>
<button onClick={e => this.clacNumber(9, false)}>-9</button>
</div>
)
}
}
// connect() 返回值是一个高阶组件
// function mapStateToProps(state) {
// return {
// counter: state.counter
// }
// }
const mapStateToProps = (state) => ({ counter: state.counter })
const mapDispatchToProps = (dispatch) => ({
addNumber: num => dispatch(addNumberAction(num)),
subNumber: num => dispatch(subNumberAction(num))
})
export default connect(mapStateToProps, mapDispatchToProps)(About)
connect():
dispatch
到 props通过发起action
将请求的数据保存到store中
action方法:
export const changeBannersAction = (banners) => ({
type: actionTypes.CHANGE_BANNERS,
banners
})
export const changeRecommendsAction = (recommends) => ({
type: actionTypes.CHANGE_RECOMMENDS,
recommends
})
import * as actionTypes from './constants'
const initialState = {
counter: 100,
banners: [],
recommends: []
}
function reducer(state = initialState, action) {
switch (action.type) {
case actionTypes.CHANGE_BANNERS:
return { ...state, banners: action.banners }
case actionTypes.CHANGE_RECOMMENDS:
return { ...state, recommends: action.recommends }
default:
return state
}
}
export default reducer
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import axiOS from 'axios'
import { changeBannersAction, changeRecommendsAction } from '../store/actionCreators'
export class CateGory extends PureComponent {
componentDidMount() {
// 发送请求
axios.get('Http://123.207.32.32:8000/home/multidata').then(res => {
const banners = res.data.data.banner.list
const recommends = res.data.data.recommend.list
this.props.changeBanners(banners)
this.props.changeRecommends(recommends)
})
}
render() {
return (
<div>
<h2>Category Page</h2>
</div>
)
}
}
const mapDispatchToProps = (dispacth) => ({
changeBanners: banners => dispacth(changeBannersAction(banners)),
changeRecommends: recommends => dispacth(changeRecommendsAction(recommends))
})
export default connect(null, mapDispatchToProps)(Category)
上面的代码有一个缺陷:
如何将异步请求交给 Redux?
{ type: CHANGE_COUNTER, num: 10 }
redux-thunk 做了什么呢
dispatch(action函数)
中的action可以是一个函数;代码演示:
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"
// 正常情况下 store.dispatch(object)
// 想要派发函数 store.dispatch(function)
// applyMiddleware 可以传入多个中间件,","隔开
const store = createStore(reducer, applyMiddleware(thunk))
export default store
import * as actionTypes from './constants'
import axios from 'axios'
export const changeBannersAction = (banners) => ({
type: actionTypes.CHANGE_BANNERS,
banners
})
export const changeRecommendsAction = (recommends) => ({
type: actionTypes.CHANGE_RECOMMENDS,
recommends
})
export const fetchHomeMultidataAction = () => {
// 如果是一个普通的action,需要返回action对象
// 问题: 对象中不能直接拿到从服务器请求的异步数据
// redux 不允许返回一个函数,需要中间件
return (dispatch, getState) => {
// console.log(getState().counter) // 100
// 进行异步操作: 网络请求
axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
const banners = res.data.data.banner.list
const recommends = res.data.data.recommend.list
// dispatch({type: actionTypes.CHANGE_BANNERS, banners})
// dispatch({type: actionTypes.CHANGE_RECOMMENDS, recommends})
dispatch(changeBannersAction(banners))
dispatch(changeRecommendsAction(recommends))
})
}
}
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { fetchHomeMultidataAction } from '../store/actionCreators'
export class Category extends PureComponent {
componentDidMount() {
this.props.fetchHomeMultidata()
}
render() {
return (
<div>
<h2>Category Page</h2>
</div>
)
}
}
const mapStateToProps = state => ({
counter: state.counter
})
const mapDispatchToProps = (dispacth) => ({
fetchHomeMultidata: () => dispacth(fetchHomeMultidataAction())
})
export default connect(mapStateToProps, mapDispatchToProps)(Category)
redux可以方便的对状态进行跟踪和调试
安装该工具需要两步:
默认该工具是未开启的,开发环境开启需要进行配置,生产环境千万千万不要打开哦!!!
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"
// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
export default store
复制代码
正常情况下,我们的 store 中应该是有不同状态的数据,比如:购物车、用户信息等等, 如果将所有的状态都放到一个reducer中进行管理,随着项目的日趋庞大,必然会造成代码臃肿、难以维护。因此,我们可以对reducer进行拆分。
以上面提到的案例为例,抽取一个 counter 的reducer和一个 home 的reducer,再将其合并起来
分不同的模块,每个模块都包含自己的核心:
>reducer:接收action对象,返回最新的state
在 index.js
中导入每一个模块的内容,通过combineReducers
合并之后放入createStore
import { createStore, applyMiddleware, compose, combineReducers } from "redux"
import thunk from "redux-thunk"
import counterReducer from './counter'
import homeReducer from './home'
import userReducer from './user'
// 将reducer合并到一起
const reducer = combineReducers({
counter: counterReducer,
home: homeReducer,
user: userReducer
})
// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
export default store
combineReducers 如何实现合并呢?
// combineReducers 原理
function reducer(state = {}, action) {
// 返回一个对象,store中的state
return {
counter: counterReducer(state.counter, action),
home: homeReducer(state.home, action),
user: userReducer(state.user, action)
}
}
到此这篇关于一文详解React Redux使用方法的文章就介绍到这了,更多相关React Redux使用内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: 一文详解React Redux使用方法
本文链接: https://www.lsjlt.com/news/168374.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0