状态机redux--声明同步动作(相当于vuex中的mutations)
(1)创建react和react-redux的基本命令:
【1】初始化项目:npx create-react-app redux-app(redux-app表示创建的项目文件名)
【2】安装redux:cnpm install redux
【3】安装react-redux:cnpm install react-redux
(2)是什么?:
类似于Vue中的vuex状态机,主要用来维护共享的状态。组件内部一般用来维护私有状态(如:用户的登录信息、系统配置信息等;)
(3)redux的核心概念: redux内部维护了三个属性,分别是state、action和reducer。
【1】state--用来存放状态,类似于vuex中的state
【2】action--表示动作,用来描述要做什么的对象,对象中包含两个属性,一个是type,一个是payload。type用来表示动作的名称(一般在组件中通过switch-case循环条件来实现对action类型的判断);payload表示调用状态机的组件传递过来的参数;
【3】reducer--本身是一个函数,该函数接收两个参数,一个是state,一个是action;返回值是修改后的state,即实现state中的数据和payload中的数据结合在一起。
(4)状态机的创建:一般可以新建一个store文件夹,在该文件夹下新建状态机。
【1】通过
createStore()方法创建状态机,通过
combineReducers()方法实现将多个reducer函数注入到状态机中去。(如果只有一个reducer函数可以直接通过createStore('reducer')创建状态机)
import { createStore, combineReducers} from 'redux'
// 声明一个reducer
function todo(state = { list: [], loading: false }, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
list: state.list.concat(action.payload),
}
case 'DELETE_TODO':
// 通过lodash中的remove方法实现对选中id状态的删除
// lodash中的remove方法是将不符合条件的数据提取出去,形成一个新的数组
// 因此剩下的state中的数据就是不用删除的数据
_.remove(state.list, item => item.id === action.payload);
return {
...state,
}
default:
return state;
}
}
// 第二个状态机
function user(state = {}, action) {
return user;
}
// 创建状态机
const store = createStore(combineReducers({ todo, user}),);
export default store;
(5)react与redux结合
【1】在index.js文件中实现对状态机的使用(index.js文件是项目的入口文件)
// 引入状态机
import store from './store'
// 引入react-redux中的Provider
import {Provider} from 'react-redux'
ReactDOM.render(
<React.StrictMode>
// 使用状态机
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
【2】在单个模块的页面中实现对状态机的使用,同时将修改后的数据分发到状态机的state中,实现页面的更新。
【3】通过react-redux中的
connect()高阶组件HOC实现对状态机的连接,完成将状态机中的状态传递到使用状态机的组件模块中。
【注意:】connect()(函数组件名)高阶组件HOC中有可以有两个参数,一个是state的回调函数,用于传递store中的状态;另一个是(可选)mapDispatchToProps的回调函数,用于动作的分发,和状态的更新。(下面会提到两种redux分发action的两种办法,其中一种就是该参数)
【使用状态机的组件代码:】
import { connect } from 'react-redux'
import { createRef } from 'react'
function Todo(props) {
const dispatch = props.dispatch;
const _input = createRef()
// 提交方法--提交状态到状态机中,实现状态机中状态的更新
const submitHandler = (event) => {
event.preventDefault();
let todo = {
id: new Date().getTime(),
name: _input.current.value
}
// 如何将todo加入到状态机?--通过dispatch分发
dispatch({ type: 'ADD_TODO', payload: todo })
}
// 删除方法--删除状态机中的状态,实现状态机中状态的更新
const delHandler = (event, id) => {
event.preventDefault();
dispatch({ type: 'REMOVE_TODO', payload: id })
}
return (
<div>
<h2>待办事项</h2>
<div>
<form onSubmit={submitHandler}>
待办 <input type="text" ref={_input} />
<input type="submit" value="提交" />
</form>
</div>
<ul>
{
props.todos.list.map(item => {
return (
<li key={item.id}>
{item.name}
<a href="" onClick={(event) => {
delHandler(event, item.id)
}}>删除</a>
</li>
)
})
}
</ul>
</div>
)
}
export default connect(
(state) => {
return state;
},
)(Todo);
(6)状态机中状态分发的两种形式:
【1】在函数组件中直接通过参数props调用dispatch方法来进行分发。
const dispatch = props.dispatch;
const delHandler = (event, id) => {
event.preventDefault();
dispatch({ type: 'REMOVE_TODO', payload: id });
};
【2】在connect()()高阶组件中,通过回调函数的方式实现对状态机中状态的分发。同时,在函数组件的内部对对应的分发函数进行调用。
const delHandler = (event,id)=>{
event.preventDefault();
// 调用状态机修改操作
props.removeTodo(id);
}
export default connect(
(state)=>{
return state;
},
// mapDispatchToProps
(dispatch)=>{
return {
addTodo(todo){
dispatch({type:'ADD_TODO',payload:todo})
},
removeTodo(id){
dispatch({type:'REMOVE_TODO',payload:id})
}
}
}
)(Todo);