Redux核心思想
- Redux 是一个 JavaScript 状态管理库,被广泛用于构建用户界面和处理应用程序的状态。它的核心思想是单一数据源(Single Source of Truth)和状态不可变性(Immutability)。
- Redux 的单一数据源意味着整个应用程序的状态被存储在一个单一的 JavaScript 对象中,称为 “store”。这个 store 是不可变的,即一旦创建,就不能直接修改它的状态。所有的状态变化都需要通过发起一个称为 “action” 的简单对象来描述。
- Redux 遵循一个严格的数据流向模式,这个模式被称为 “Flux” 架构。当应用程序的状态需要更新时,开发者需要 dispatch(派发)一个 action 到 Redux store。然后,Redux 根据 action 的类型和附带的数据来计算新的状态,并用新的状态替换旧的状态。
- 这种通过派发 action 和计算新状态的过程是纯函数式的,也就是说,它完全根据输入的数据来计算输出的结果,没有任何副作用。这种函数式的设计使得 Redux 在应用程序开发中具有可预测性和可测试性。
- 另一个核心思想是使用纯函数来处理状态的变化,这些函数被称为 “reducers”。Reducer 接收先前的状态和一个 action,返回一个新的状态。通过将这些 reducer 组合在一起,我们可以构建一个复杂的状态管理系统。
- Redux 还支持 “middleware“,用于在处理 action 和 reducer 之间添加自定义的逻辑。这使得开发者可以在应用程序中处理异步操作、日志记录、错误处理等。
- 总而言之,Redux 的核心思想是通过一个单一的不可变状态来管理应用程序的状态,并使用纯函数式的方式处理状态的变化。这种设计模式使得状态管理更加可控、可预测,并且易于测试。
Redux基本使用
创建 Redux store
1)引入createStore
2)初始化数据
3)定义reducer函数(纯函数)
4)创建store
const { createStore } = require('redux')
// 初始化数据
const initState = {
name: 'colin',
age: 21
}
// 定义reducer函数:纯函数
function reducer() {
return initState
}
// 创建store
const store = createStore(reducer)
module.exports = store
- 使用store(使用node跑一下)
const store = require('./store');
console.log(store.getState())
修改store的数据
1)新创建一个changeStore文件写修改方式
const store = require('./store');//先拿store
//修改1
const nameAction1 = { type: "change_name", name: "colin1" }
store.dispatch(nameAction1) //派发
console.log(store.getState())
//修改2
const nameAction2 = { type: "change_age", age: 18 }
store.dispatch(nameAction2) //派发
console.log(store.getState())
2)在reducer函数里做修改
// 定义reducer函数:纯函数
// 接收两个参数,(1)store目前保存的值 (2)传入的action
// 返回值:作为store之后存储的state
function reducer(state = initState, action) {
// 有新数据进行更新,返回新的state
// 没有 返回之前的state
if (action.type === 'change_name') {
return { ...state, name: action.name } //第一次修改name (nameAction1)
} else if (action.type === 'change_age') {
return { ...state, age: action.age } //第二次修改age (nameAction2)
}
return initState
}
store数据订阅和reducer优化判断
1)通过订阅拿数据
store.subscribe(() => {
console.log('订阅', store.getState());
})
2)优化reducer
function reducer(state = initState, action) {
// 有新数据进行更新,返回新的state
// 没有 返回之前的state
switch (action.type) {
case 'change_name':
return { ...state, name: action.name }
case 'change_age':
return { ...state, age: action.age }
default:
initState
}
}
代码重构-优化
1)重构action -> 函数式
const store = require('./store');
store.subscribe(() => {
console.log('订阅', store.getState());
})
// actionCreators 帮助我们创建action
const changeNameAction = (name) => ({
type: 'change_name',
name: name
})
const changeAgeAction = (age) => ({
type: 'change_age',
age: age
})
const nameAction1 = changeNameAction('colin1')
const nameAction2 = changeNameAction('colin2')
store.dispatch(nameAction1)
store.dispatch(nameAction2)
const nameAction3 = changeAgeAction(18)
const nameAction4 = changeAgeAction(17)
store.dispatch(nameAction3)
store.dispatch(nameAction4)
React结合Redux
创建store文件夹,初始化index.js、reducer.js
import { createStore } from "redux";
import reducer from "./reducer";
const store = createStore(reducer)
export default store
const initState = {
count: 0
}
function reducer(state = initState,action){
switch(action.type){
default:
return state
}
}
export default reducer
组件中引用(重构前需要每个文件都引用一次非常麻烦)
export class Main extends PureComponent {
componentDidMount() {
store.subscribe(() => {
const state = store.getState()
this.setState({ counter: state.count }) //订阅:更新即变化
})
}
constructor() {
super()
this.state = {
counter: store.getState().count //初始化counter
}
}
render() {
const { counter } = this.state
return (
<div>
<h2>Main counter: {counter}</h2>
<div className='pages'>
<Home /> //子组件
<Profile />//子组件
</div>
</div>
)
}
}
定义 Action 类型(Types):
export const ADD_NUMBER = 'add_number';
export const SUB_NUMBER = 'sub_number';
创建Action创建函数(Action Creator)
import * as actionTypes from './constants';
export const addNumberAction = (num) =>({
type:actionTypes.ADD_NUMBER,
num
})
export const subNumberAction = (num) =>({
type:actionTypes.SUB_NUMBER,
num
})
创建reducer
function reducer(state = initState, action) {
switch (action.type) {
case actionTypes.ADD_NUMBER:
return { ...state, count: state.count + action.num }
case actionTypes.SUB_NUMBER:
return { ...state, count: state.count - action.num }
default:
return state
}
}
组件内使用
addNumber(num) {
store.dispatch(addNumberAction(num))//派发
}
render() {
const { count } = this.state
return (<div>
<div>Home count: {count}</div>
<div style={{ marginTop: 20 + 'px' }}>
<button onClick={() => this.addNumber(1)}>+1</button>
<button onClick={() => this.addNumber(3)}>+3</button>
<button onClick={() => this.addNumber(5)}>+5</button>
</div>
</div>
)
}
react-redux
provider
在应用程序的入口文件中,创建 Redux store 并将其提供给应用程序。使用 Redux 的 createStore 函数创建 store,并使用 react-redux 的 Provider 组件将 store 包裹在应用程序的顶层组件外部。例如:
root.render(
<Provider store={store}>
<App />
</Provider>
)
connect
在需要访问 Redux store 中的状态或触发 action 的组件中,使用 react-redux 的 connect 函数来连接组件与 Redux store。在组件的文件中,导入需要连接的组件以及需要从 store 中获取的状态和需要派发的 action。然后,使用 connect 函数将组件与 Redux store 进行连接,并导出连接后的组件。例如:
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
export class About extends PureComponent {
render() {
const { count } = this.props
return (
<div>About:{count}</div>
)
}
}
function fn(state) {
return {
count: state.count
}
}
export default connect(fn)(About)
组件和dispatch连接起来
export class About extends PureComponent {
click(num) {
if (num >= 0) {
this.props.addNumber(num) //这里能拿到fn2的方法并传值
} else {
this.props.subNumber(num)
}
}
render() {
const { count } = this.props
return (<div>
<div>About:{count}</div>
<div style={{ marginTop: 20 + 'px' }}>
<button onClick={() => this.click(100)}>+100</button>
<button onClick={() => this.click(-50)}>-50</button>
</div>
</div>
)
}
}
function fn2(dispatch) { //这里dispatch
return {
addNumber(num) {
dispatch(addNumberAction(num))
},
subNumber(num) {
dispatch(subNumberAction(num))
}
}
}
function fn(state) {
return {
count: state.count
}
}
export default connect(fn, fn2)(About)
Redux的异步操作
第一种:异步请求和保存数据到state(组件内:不太合适)
参考React结合Redux(创建变量、创建action creator、书写reducer)
// reducer
function reducer(state = initState, 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 const CHANGE_BANNERS = 'change_banners'
export const CHANGE_RECOMMENDS = 'change_recommends'
// 创建action creator
export const changeBannersAction = (banners) => ({
type: actionTypes.CHANGE_BANNERS,
banners
})
export const changeRecommendsAction = (recommends) => ({
type: actionTypes.CHANGE_RECOMMENDS,
recommends
})
发送请求获取数据->更改state数据(dispatch)
import axios from 'axios'
import React, { PureComponent } from 'react'
import { connect } from 'react-redux';
import store from '../../store'
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
console.log(banners, recommends);
//第一种写法
// store.dispatch(changeBannersAction(banners))
// store.dispatch(changeRecommendsAction(recommends))
// 第二种写法 用 mapDispatchProps()
this.props.changeBanners(banners)
this.props.changeRecommends(recommends)
})
}
render() {
return (<div>
<div>Category</div>
</div>
)
}
}
function mapDispatchProps(dispatch) {
return {
changeBanners(banners) {
dispatch(changeBannersAction(banners))
},
changeRecommends(recommends) {
dispatch(changeRecommendsAction(recommends))
}
}
}
// 第一个函数没有 直接传null 必须传
export default connect(null, mapDispatchProps)(Category);
另一个组件借用数据
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumberAction, subNumberAction } from '../../store/actionCreators';
export class About extends PureComponent {
render() {
const { banners, recommends } = this.props
return (<div>
<div className='banner'>
<span>轮播图数据</span>
<ul>
{banners.map((v, index) => {
return <li key={index}>{v.title}</li>
})}
</ul>
</div>
<div className='recommend'>
<span>推荐数据</span>
<ul>
{recommends.map((v, index) => {
return <li key={index}>{v.title}</li>
})}
</ul>
</div>
</div>
)
}
}
function fn(state) {
return {
count: state.count,
banners: state.banners,
recommends: state.recommends
}
}
export default (fn)(About)
第二种:异步请求redux-thunk (redux内 :合适)
安装 Redux Thunk: 首先,使用 npm 或 yarn 安装 Redux Thunk 中间件:
npm install redux-thunk //有了这个东西 我们就可以 让dispatch接收派发一个函数而不仅仅是一个对象
// 那我们不就可以异步请求了吗? 我们将这个函数作为返回值一并传给组件
应用 Redux Thunk 中间件: 在创建 Redux store 时,使用 Redux 的 applyMiddleware 函数将 Redux Thunk 中间件应用于 store。例如:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
创建Action函数
export const fetchHomeMultidataAction = () => {
return function foo(dispatch, getState) { //两个参数
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(changeBannersAction(banners)) //派发
dispatch(changeRecommendsAction(recommends))
})
}
}
调用Action
const mapDispatchProps = (dispatch) => ({
fetchHomeList(){
dispatch(fetchHomeMultidataAction())
}
})
redux-devtool
Edge安装地址
开启(store文件夹下inde.js文件)
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// 如果有中间件 包裹起来
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
reducer的模块拆分
当应用程序变得复杂时,将reducer拆分成多个模块可以更好地组织代码。这可以通过使用Redux提供的combineReducers函数来实现。以下是一个示例,展示了如何在模块之间划分reducer:
首先,创建一个名为reducers.js
的文件,用于存放所有的reducer模块
import { combineReducers } from 'redux';
import counterReducer from './counterReducer';
import todosReducer from './todosReducer';
import userReducer from './userReducer';
const rootReducer = combineReducers({
counter: counterReducer,
todos: todosReducer,
user: userReducer,
});
export default rootReducer;
创建每个reducer模块的文件。例如,创建一个名为counterReducer.js
的文件,其中包含与计数器相关的reducer逻辑。
import { INCREMENT, DECREMENT } from './actionTypes';
const initialState = {
count: 0,
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case INCREMENT:
return {
...state,
count: state.count + 1,
};
case DECREMENT:
return {
...state,
count: state.count - 1,
};
default:
return state;
}
};
export default counterReducer;
同样地,创建其他reducer模块的文件,并实现相应的reducer逻辑。
最后,将rootReducer导入到应用程序的store.js文件中,并使用combineReducers函数来组合所有的reducer模块。
javascript
Copy code
// store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
最后将rootRedycer
导入到应用程序的store.js
文件,并使用combineReducers
函数来组合所有reducer模块
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
通过以上步骤,您可以将reducer划分为多个模块,并在应用程序中使用rootReducer来创建Redux store。每个模块可以独立处理特定的状态,并通过combineReducers将它们组合在一起,形成一个根级的reducer函数。
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END