一、需求描述及分析
在一个页面中有三个组件,分别为Header
、List
以及AddForm
,它们是兄弟组件,要求:
Header
组件中有两个按钮(一个同步一个异步)可以打开AddForm
(默认AddForm隐藏不显示)AddForm
有一个按钮可以关闭自身AddForm
中有输入框,可以添加数据到List
组件List
可以显示数据
需求中的数据流如下图所示:
二、案例实操
1. 安装依赖
- 安装redux(核心库)
npm install redux
- 安装react-redux(用于实现React中的props与redux的映射)
npm install react-redux
- 安装redux-thunk(用于支持异步action)
npm install --save redux-thunk
- 安装redux-devtools-extension(用于在开发者工具中查看状态变化)
npm install redux-devtools-extension -D
2. 项目目录
3. 准备action的type名称的常量
constant.js
/*
该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
*/
export const OPEN_FORM = 'openForm'
export const CLOSE_FORM = 'closeForm'
export const ADD_DATA = 'addData'
4. 编写action的生产工厂
src\store\actions\form.js
/*
该文件专门为Form组件生成相关action对象
*/
import {OPEN_FORM,CLOSE_FORM} from '../constant'
// 同步action,就是指action的值为Object类型的一般对象
export const openForm = data => ({type:OPEN_FORM,data}) //打开添加表单的同步action对象
export const closeForm = data => ({type:CLOSE_FORM,data}) // 关闭表单的同步action
// 异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
export const openFormAsync = data => {
// 返回一个函数,参数是dispatch函数,函数里面包含异步操作,且一般要调用同步action
return (dispatch) => {
setTimeout(() => {
dispatch(openForm(data))
},2000)
}
}
src\store\actions\list.js
/*
该文件专门为List组件生成相关action对象
*/
import {ADD_DATA} from '../constant'
// 同步action
export const addData = data => ({type:ADD_DATA,data})
5. 编写reducers
当有多个
reducer
时,需要使用combineReducers()
函数将其合并为一个
src\store\reducers\form.js
import {OPEN_FORM,CLOSE_FORM} from '../constant'
/**
* form组件的reducer
*/
// 初始状态
let initSate = {
isOpen:false //是否打开表单
}
export default function formReducer(state=initSate,action){
console.log(action)
switch(action.type){
case OPEN_FORM: // 显示表单
return {...state,isOpen:true} // 注意更新状态的写法
case CLOSE_FORM: // 隐藏表单
return {...state,isOpen:false}
default:
return state
}
}
src\store\reducers\list.js
import {ADD_DATA} from '../constant'
// 初始状态
const initSate = {
list:[]
}
export default function listReducer(state=initSate,action){
switch(action.type){
case ADD_DATA:
return {...state,list:[...state.list,action.data]} // 注意更新状态的写法
default:
return state
}
}
src\store\reducers\index.js
/*
该文件用于汇总所有的reducer为一个总的reducer
*/
//引入combineReducers,用于汇总多个reducer
import {combineReducers} from 'redux'
//引入为form组件服务的reducer
import form from './form'
//引入为list组件服务的reducer
import list from './list'
//汇总所有的reducer变为一个总的reducer
export default combineReducers({
form,
list
})
6.编写store
src\store\store.js
/*
该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/
// 引入createStore,专门用于创建Redux最为核心的store对象
import {applyMiddleware, createStore} from 'redux'
// 引入汇总后的reducer
import reducer from './reducers'
// 引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'
// 引入redux开发工具
import {composeWithDevTools} from 'redux-devtools-extension'
// 暴露store,并将对开发者工具和redux-thunk的支持添加进去
export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
到目前为止,都是在使用
Redux
的提供的api,并未使用react-redux
7. 更改入口文件
src\index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux'
import store from './store/store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,
document.getElementById('root')
);
要使用
Provider
组件包裹App
组件,并将store
对象作为props传递进去,此api由react-redux
提供
8. 编写各个组件
使用
react-redux
提供的connect
函数将普通组件包装为容器
关于connect:https://segmentfault.com/a/1190000015042646
src\components\Main\Header.jsx
import React, { Component } from 'react'
// 引入connect高阶函数
import {connect} from 'react-redux'
// 引入form的action对象,进而操作form的显示与隐藏
import {openForm,openFormAsync} from '../../store/actions/form'
class Header extends Component {
/**
* 同步打开表单
*/
openForm = () => {
// 在props中调用映射后的函数,即可更改状态
this.props.openForm(null)
}
/**
* 异步打开表单
*/
openFormAsync = () => {
this.props.openFormAsync(null)
}
/**
* render
* @returns
*/
render() {
return (
<div>
<h3>Header</h3>
<button onClick={this.openForm}>同步打开表单</button>
<button onClick={this.openFormAsync}>异步打开表单</button>
</div>
)
}
}
// 通过connect函数进行包装
export default connect(
state => ({
}),
{openForm,openFormAsync} // 第二个参数传入对象可省略不写dispatch函数
)(Header)
src\components\Main\List.jsx
import React, { Component } from 'react'
// 引入react-redux的connect函数
import {connect} from 'react-redux'
class List extends Component {
render() {
return (
<div>
<h3>List</h3>
<ul>
{
this.props.list.map(item => {
return <li>{item}</li>
})
}
</ul>
</div>
)
}
}
export default connect(state => ({list:state.list.list}) // 状态映射
,
{}) // 操作映射
(List)
src\components\Main\AddForm.jsx
import React, { Component } from 'react'
// 引入react-redux的connect函数
import {connect} from 'react-redux'
// 引入相关action,用于状态更新
import {closeForm} from '../../store/actions/form'
import {addData} from '../../store/actions/list'
class AddForm extends Component {
/**
* 组件状态
*/
state = {
inputValue : ''
}
/**
* 关闭表单
*/
closeForm = () => {
// 在props中调用更新redux状态的函数
this.props.closeForm(null);
}
/**
* 添加数据
*/
addData = () => {
this.props.addData(this.state.inputValue)
}
/**
* 处理输入框变化
*/
handleChange = (e) => {
this.setState({inputValue:e.target.value})
}
render() {
console.log(this.props.isOpen)
if(this.props.isOpen){
return (
<div>
<h3>Form</h3>
<input onChange={e => this.handleChange(e)}/>
<button onClick={this.addData}>添加</button>
<button onClick={this.closeForm}>关闭表单</button>
</div>
)
} else {
return (
<div/>
)
}
}
}
export default connect(
state => ({
isOpen:state.form.isOpen // 将props与redux中的状态建立映射关系
}),
{closeForm,addData}
)(AddForm)
AddForm
组件所展示的写法更全面一些
src\components\Main\index.jsx
import React, { Component } from 'react'
import AddForm from './AddForm'
import Header from './Header'
import List from './List'
import './index.scss'
export default class Main extends Component {
render() {
return (
<div className='main'>
<Header/>
<br/>
<List/>
<br/>
<AddForm/>
</div>
)
}
}
9. 效果展示
Header
控制AddForm
的显示(同步+异步)
AddForm
关闭自身
AddForm
添加数据
使用函数钩子:https://blog.csdn.net/weixin_47967031/article/details/120155872