上一篇:【react】react18的学习(三)–hooks组件
1、父子通信:props、ref
父传子:props
- 传给子组件状态数据;
- 特殊地,
props.children
:将元素内容传给子组件;所以不要手动给子组件设置属性,会被标签内容覆盖; - 通过给子组件
ref
,获取子组件数据;
子传父
- 调用父组件传来的方法,修改父组件的状态或传值;
2、祖先通信:上下文对象,跟props属性无关
包括父子、及更深层嵌套,就是借助一个共同组件上下文
借助上下文对象:类似vue事件总线this.$bus
与provider+inject
的结合,定义上下文对象,在祖先组件使用并传值,子孙组件使用;
1、创建指定上下文对象
// voteContext.js
import React from 'react'
const VoteContext = React.createContext()
export default VoteContext
2、类组件使用:祖先组件提供方式1种;子孙组件使用方式2种
- 祖先组件中导入:1种
// 祖先组件中导入
import VoteContext from '@/contexts/voteContext'
// 祖先组件使用并提供公共数据
<VoteContext.Provider
value={{
supNum,
oppNum,
toSup: this.toSup,
toOpp: this.toOpp,
}}
>
<div>
<div>总人数:{sum}</div>
<Main></Main>
<Footer></Footer>
</div>
</VoteContext.Provider>
- 后代组件使用:2种
// 后代组件使用
import VoteContext from '@/contexts/voteContext'
// 使用方式1:绑定到静态属性,类似属性校验
static contextType = VoteContext
let { supNum, oppNum } = this.context
// 使用方式2:标签方式
<VoteContext.Consumer>
{context => {
let { } = context
return ()
}}
</VoteContext.Consumer>
3、hooks组件使用
与类组件一样,区别就是使用hooks函数组件语法;且后代组件也有两种方式:标签、useContext
;
- 祖先组件中导入:1种
// 祖先组件中导入
import VoteContext from '@/contexts/voteContext'
// 祖先组件使用并提供公共数据
<VoteContext.Provider
value={{
supNum,
oppNum,
change,
}}
>
<div>
<div>总数:{sum}</div>
<Main></Main>
<Footer></Footer>
</div>
</VoteContext.Provider>
- 后代组件使用:2种
// 后代组件使用
import VoteContext from '@/contexts/voteContext'
// 使用方式1:hooks函数
import React, { useContext } from 'react'
const { change } = useContext(VoteContext)
// 使用方式2:标签方式
<VoteContext.Consumer>
{context => {
let { } = context
return ()
}}
</VoteContext.Consumer>
3、任意组件通信:状态管理
一般使用props、状态管理,比较常用,上下文比较难维护;
状态管理库方案:纯redux
、redux与react-redux
、redux-toolkit与react-redux
、mobx
等
方案一:纯redux
redux,不仅可以用于react,而在react中使用时,可以借助官方
react-redux
来简化使用操作;
- 第一步:编写对应reducer,即公共状态与处理方法;类似vuex中modules
let initial = {
supNum: 10,
oppNum: 0,
}
export default function voteReducer(preState = initial, action) {
let { type } = action
switch (type) {
case 'VOTE_SUP':
preState.supNum++
break
case 'VOTE_OPP':
preState.oppNum++
break
default:
break
}
return preState
}
- 第二步:创建公共容器
store
,存储公共数据+事件池
;分别对应reducer里的state、action
import { createStore } from 'redux'
import voteReducer from './reducer/voteReducer'
const store = createStore(voteReducer)
export default store
- 第三步:让组件能够使用
store
;借助上下文通信方式;getState()
:从仓库中获取公共状态;
import ThemeContext from './ThemeContext'
import store from './store'
root.render(
<ConfigProvider locale={zhCN}>
<ThemeContext.Provider value={{ store }}>
<Vote></Vote>
</ThemeContext.Provider>
</ConfigProvider>
// 严格模式,会渲染两次,对不安全函数会报红色警告也能执行
// <React.StrictMode></React.StrictMode>
)
// hooks子组件获取
const { store } = useContext(ThemeContext)
const { supNum, oppNum } = store.getState()
- 第四步:让组件能够操作
store
;dispatch({})派发
:执行仓库中的Reducer
,对公共状态进行操作,redux内部初始化会执行一次用于首次初始化状态;
onClick={() => {
store.dispatch({ type: 'VOTE_SUP' })
}}
- 第五步:让组件能够更新
store
;subscribe(()=>{})订阅
:订阅方法存放在事件池
中,当仓库中公共状态被修改后,到方法池执行方法更新组件;
// 让组件更新:setState、useState
// 所以需要给store一个方法,store中状态更新会执行这个方法,这个方法触发组件更新
const [aa, setaa] = useState(0)
useEffect(() => {
//该方法返回unsubscribe方法,不用传参,用于移除当前订阅
store.subscribe(() => {
setaa(pre => {
return pre + 1
})
})
}, [])
redux 工程化开发(3步)
combineReducers
:合并各个模块reducer;
第一步:各模块reducer
统一管理;(类似vuex modules)
// store/reducers/index.js
// redux工程化管理
import { combineReducers } from 'redux'
import voteReducer from './voteReducer'
const reducer = combineReducers({
vote: voteReducer,
})
export default reducer
// store/index.js
import { createStore } from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
export default store
// 组件使用
const { store } = useContext(ThemeContext)
const { supNum, oppNum } = store.getState().vote
补充:按模块化,使用
不足1:派发时:每个模块的reducer都会被找一遍;且不同模块
action.type
可能会冲突;不足2:订阅后:更改某个模块状态,事件池中所有模块订阅的方法还是会遍历执行,导致所有组件更新,性能不好;
第二步:派发行为标识TYPE的统一管理;针对第二步不足1;
将action的type,都用模块名前缀统一命名管理,避免重复;
// store/action-types.js
export const VOTE_SUP = 'VOTE_SUP'
export const VOTE_OPP = 'VOTE_OPP'
创建reducer、组件派发时都可以使用变量
import * as TYPES from '../action-types'
switch (type) {
case TYPES.VOTE_SUP:
preState.supNum++
break
-----
import { VOTE_OPP, VOTE_SUP } from '../../store/action-types'
store.dispatch({ type: VOTE_SUP })
第三步:组件派发操作做统一管理;
类似定义reducer,每个模块定义voteAction.js,内部定义方法,返回{trpe:‘’},并最终合并成一个action;
// store/actions/voteAction.js
import * as TYPES from '../action-types'
const voteAction = {
sup() {
return { type: TYPES.VOTE_SUP }
},
}
export default voteAction
// store/actions/index.js
import voteAction from './voteAction'
const action ={
vote: voteAction,
}
export default action
// 组件使用
import action from '../../store/actions'
onClick={() => {
store.dispatch(action.vote.sup())
}}
方案二:redux&react-redux
基于react对redux
进行封装,主要用于简化redux在组件中的使用,而不是公共状态的创建;
步骤:
- 第一步:redux工程化方式创建store,且reducer中返回的state需要深拷贝,否则当作状态没变不更新组件;
- 第二步:将store传递给子组件,使用 react-redux 提供的
Provider
标签; - 第三步:子组件通过
connect()()
将状态、dispatch操作绑定到props上,进行操作即可;
// 导入store
import { Provider } from 'react-redux'
import store from './store'
---
<ConfigProvider locale={zhCN}>
<Provider store={store}>
<Vote></Vote>
</Provider>
</ConfigProvider>
// 子组件
import React from 'react'
import { connect } from 'react-redux'
const Main = props => {
const { supNum, oppNum, sup } = props
return (
<div className="main">
<div>支持人数:{supNum}</div>
<div>反对人数:{oppNum}</div>
<Button type="primary" onClick={sup}>
支持
</Button>
</div>
)
}
export default connect(state => state.vote,action.vote)(Main)
----
// 派发操作:相当于将
sup() {
return { type: TYPES.VOTE_SUP }
},
//转为
sup(dispatch) {
dispatch({ type: TYPES.VOTE_SUP })
},
优点:
-
不需要手动订阅;
-
组件只在所使用状态更改时才更新;
方案三:redux-toolkit与react-redux
具体参见:【react】react18的学习(七)–redux-toolkit
方案四:mobx
mobx具体内容:【react】react18的学习(八)–mobx介绍