3 组件通信
约 637 个字 186 行代码 预计阅读时间 4 分钟
1 父子组件通信
- Father-to-Son
通过
props
进行传递 - Son-to-Father
通过
props
中的函数传递(函数在父组件中实现) => State 在何处,操作 State 的 method 就在何处
2 任意组件间通信
2.1 消息订阅与发布
此处使用
PubSub.js
实现,需要提前安装:npm i pubsub-js --save
import PubSub from 'pubsub-js' // 引入 @ both
// 订阅消息
componentDidMount() {
PubSub.subscribe('msgName',(_,data) => { // _ 是占位符号
this.setState({...data}) // 一个解构赋值的骚操作
})
}
// 发布消息 @ any Function
function (){
const dataobj = {
name: '',
age: 12
}
PubSub.publish('msgName', dataObj)
}
2.2 Redux
Redux 用于管理多个组件的共享状态,中文文档 首先需要安装
npm i redux --save
开发者工具
- 安装
npm i redux-devtools-extension
- 在
store
中进行配置js // @ redux/store import {composeWithDevTools} from 'redux-devtools-extension' const store = createStore( allReducers, composeWithDevTools(applyMiddleware(thunk)) )
三个核心概念
- Action 动作对象,包含两个属性:
type
:字符串,唯一标识动作data
:任意类型,可选
- Reducer(必须是纯函数)
- 用于 初始化 / 加工 状态
- 「加工」更具 old-state & action 生成新的 state
- 不能直接操作 preState => 只能返回一个基于 preState 计算的 newState
所以不能用
preState.unshift
->return [data,...preState]
- Store - C位人士
精简版实现
我们在
/src/redux
下存放相关文件
- 创建 Store
- 创建用于计算实例的 Reducer
const initState = 0 // 当传入 undefined 时进行初始化 export default function countReducer(preState=initState, action) { const {type, data} = action // 提取 action 的相关信息 // 根据 type 对 state 进行迭代修改 switch(type) { case 'increase': return preState + data case 'decrease': return preState - data default: return preState } // 是否操作state的前置判断应该在调用 dispatch 前实现 }
- 发布更改指令
- 增加订阅 因为 store 数据的改变不回触发重新 render,所以:
完整版实现
- 添加常量模块:避免拼写错误
- 添加 ActionCreator - 生成 action 对象
- 引入并使用
异步 Action(不必要)
- 同步 Action dispatch 一个包含 type & data 的普通对象
- 异步 Action dispatch 一个 function
需要安装中间件 npm i redux-thunk
,在 store 中应用以下更改
// @ redux/store.js
import thunk from 'redux-thunk'
import {createStore,applyMiddleware} from 'redux'
export default createStore(countReducer, applyMiddleware(thunk))
// @ redux/countActionCreator.js 添加异步 action
export const createAsyncIncreaceAction = (data, time) => {
return (dispatch) => { // 返回 function
setTimeout(()=>{ // 开启延时
dispatch(createIncreaceAction(data)) // 调用同步action
}, time)
}
}
// 其实可以在组件里实现这个逻辑
2.3 react-redux
我们使用「容器组件」包裹 UI 组件,容器组件: - 直接和 Redux 交接,使用其 API(getState / dispatch) - 向UI组件提供:reudx中保存的状态 & 操作状态的方法(通过 props)
首先需要安装 npm i react-redux
,随后创建容器组件
// @ container.js,引入UI组件及连接件
import UI from './UIcomp'
import {connect} from 'react-reduc'
// 引入 action
import { createIncAction, createDecAction } from './actionCreator'
// 返回一个对象(会合并到 props 中)
function mapStateToProps(state) {
return {count: state}
}
// 返回对象,是 methods 的键值对
function mapDispatchToProps(dispatch) {
return {
add: (val) => dispatch(createIncAction(val)),
sub: (val) => dispatch(createDecAction(val))
}
}
// 两个参数分别将 data & methods 传递给 UI.props
export default connect(mapStateToProps, mapDispatchToProps)(UI)
// 可以在 UI 中使用 this.props.count / this.props.add 访问数据和方法
// store 在应用UI组件的文件中引入,并通过 props 传递
// @ UIcomponent
import store from './redux/store'
import Container from './container'
export default UI extends Component {
render() {
return (
<Container store={store}/>
)
}
}
精简版本
// 对 container 进行一个简写的大动作
export default connect(
state => ({count:state}), // 使用圆括号包裹返回的对象,避免歧义
{ // react-redux 会自动进行分发,参数会直接被 actionCreator 接收
add: createIncAction,
sub: createDecAction,
addAsync: createAsyncIncAction
}
)(UI)
-
react-redux
通过connect
监听了数据变化,在index.js
中也不用手动可进行消息订阅了 -
因为所有人都要用到 store,在使用组件的时候一个个
store={store}
实在太阴间哩,所以对 App 进行一个大的改造 -
整合 UI & Container(1v1会导致文件数量 double,所以我们尝试整到一起)
数据共享
- 所有的操作定义在 同一个 const 文件中
- 不同种类的操作应该定义独立的 actionMaker & Reducer
- 需要在
store.js
中汇总所有的 Reducer// @ store.js // 引入所有的 Reducer import{combineReducers} from 'redux' const CombinedReducer = combineReducers({ // 各个reducer操作的结果将作为 state 的对应分量 sum: countReducer, // import同名时可触发简写 list: personReducer }) // 暴露整合的 store export default createStore( CombinedReducer, applyMiddleware(thunk) )
- 进一步优化:在
redux/reducers/index.js
中引入并汇总
- 进一步优化:在
- 在初始化 Container 的时候对 state 做对应修改
2.3 后代组件通信
- 创建 Context 容器
const xxContext = Reace.createContext()
- 在 render 中使用
<xxContext>
包裹子组件,通过 value 传递数据 - 在后代组件中读取数据