文章目录
父向子
props属性
具体可看【react框架】学习记录5-组件实例的三大属性state、props、refs
context链式传递
功能和vue的provide/inject一样的,且类组件和函数组件的用法是差不多的。
首先创建一个负责传递数据的状态组件Content.jsx:
import React from 'react';
const Content = React.createContext(); // 引入API,类似定义了一个空组件
export default Content;
然后在父组件中去使用这个状态组件:
import Content from './Content.jsx'
let username = 'xiaoming'
let age = 12
// 模板中把子组件包裹起来,并传入需要传递的变量,可以以对象的形式
<Content.Provider value={{ username, age }}>
<Zizujian />
</Content.Provider>
当然,如果你想把状态维护在一个组件中也可以,上面两个合成一个文件写即可:
export const Content = React.createContext(); // 引入API,类似定义了一个空组件
let user = {
name: 'xxx',
age: 12
}
// 模板中把子组件包裹起来,并传入需要传递的变量,可以以对象的形式
<Content.Provider value={user}>
<button>点击修改userName</button>
<Zizujian />
</Content.Provider>
然后在子组件中就可以接收传进来的东西了,类组件可以这样写:
import Content from './Content.jsx' // 注意,引入的是状态组件而不是父件
export default class Zizujian extends Component {
static contextType = Content;
render() {
const { name, age } = this.context // 获取到数据
return (
<div></div>
)
}
}
在函数式子组件中可以这样使用:
import Content from './Content.jsx' // 注意,引入的是状态组件而不是父件
export default function Zizujian() {
return (
<div>
<Content.Consumer> // 改用这个获取,因为函数式组件没有this,不能const {username,age} = this.context
{(value) => { // 用函数接收数据
return `${value.name}, 年龄是${value.age}`;
}}
</Content.Consumer>
</div>
);
}
// 或者使用hooks
import Content from './Content.jsx' // 注意,引入的是状态组件而不是父件
export default function Zizujian() {
const userInfo = useContext(Content) // 这样就能拿到数据了
return (
<div></div>
);
}
和vue一样,实际开发不会使用这个
render插槽
功能和vue的solt插槽一样。先来看看普通的插槽怎么写:
// 在父组件中,同时写a和b组件
<A>
<B>xxxx</B> // 插入了b组件
</A>
// 在a组件中
<div>
{this.props.children} // 这样去渲染出来插入的标签
</div>
问题来了,这样子如果a想传数据给b组件,比较麻烦,所以react提供了render props:
// 在父组件中,同时写a和b组件
<A render={(data) => <B data={data}></B>}></A> // render可以自定义命名,入参是传入的值,返回的是一个插入的标签
// 在a组件中
<div>
{this.props.render(内部state数据)} // 这样去渲染出来插入的标签
</div>
// 在b组件中
this.props.data // 那到a传入的值
子向父
调用父级函数
父级首先通过props给子级传递一个函数:
updateApp = (info)=>{
console.log('拿到子级传过来的信息', info)
}
render() {
return (
<Zizujian updateAppState={this.updateApp}/>
)
}
子级在自己内部接收这个props传来的函数,需要传递信息的时候调用并且把信息作为入参传入即可。
兄弟/子孙之间
redux(推荐)
useReducer与useContext(别用了)
函数组件的原生hook,是官方参照redux出的简化版,可以看看使用方式。
个人认为当你需要使用到状态管理的时候,就不需要考虑这个了,直接上redux或者相关库,因为有可能后面随着业务的复杂度上升,状态管理的要求也会更多。
第三方库
pubsubJS 发布订阅(可考虑)
先安装这个库
yarn add pubsub-js
在A组件中订阅消息(接受消息):
import PubSub from 'pubsub-js' // 先引入
componentDidMount(){
// 订阅了一个叫做fn的消息
this.token = PubSub.subscribe('fn',(_, stateObj)=>{ // 回调默认传入两个参数,第一个是发布描述(不想要可以用_代替),第二个是发布的数据
this.setState(stateObj)
})
}
componentWillUnmount(){
PubSub.unsubscribe(this.token) // 记得要销毁订阅
}
在B组件中发布消息:
// 可在不同地方发布多个不同数据的消息
PubSub.publish('fn',{ a : 1 })
PubSub.publish('fn',{ b : 1 })
PubSub.publish('fn',{ c : 1 })
这个库不要太过分依赖,当页面到处都是异步操作的时候,就难控制了。
think-react-store 状态管理
找不到官方的文档地址,先暂时贴出npm地址和源码地址吧:
npm:https://www.npmjs.com/package/think-react-store
github:https://github.com/cpagejs/think-react-store
其实是一个简化版的redux,这个库非常轻量,使用也很简单。这里简单记录一下使用方式(安装就不记录了)。
可以先分模块,例如用户模块store/user.js:
export default {
state: {
id: undefined,
username: undefined
},
reducers: { // 同步操作
getUser(state, payload) {
return { // 返回一个对象,自动合并state
...state,
...payload
}
}
},
effects: { // 异步操作
async getUserAsync(dispatch, rootState, payload){
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
dispatch({
type: 'getUser',
payload
});
}
}
};
然后创建个汇总根文件store/index.js
export { default as user } from './user';
在父组件中使用:
import React, { useState } from 'react';
import { StoreProvider } from 'think-react-store';
import * as store from './stores';
import log from 'think-react-store/middlewares/log'; // 引入个中间件,就理解为引入个功能(这个中间件的功能就是自动打印出store的变化,供开发者调试)
import User from './user'; // 子组件
export default function(props){
const [state, setState] = useState()
return (
<StoreProvider store={store} middleware={[log]}> // 一定要包裹起来,传入所有store,中间件(不需要可不写)
<User />
</StoreProvider>
)
}
子组件中使用:
import React, { useState, useEffect } from 'react';
import { useStoreHook, useStateHook, useDispatchHook } from 'think-react-store';
export default function(props){
const [state, setState] = useState()
const { user: { id, username, getUserAsync } } = useStoreHook(); // 方法一,拿user模块里的东西
const states = useStateHook('user'); // 方法二,直接拿user模块里的state
// console.log(states)
const dispatchs = useDispatchHook(); // 获取派发hooks
useEffect(() => {
getUserAsync({ // 方法一的使用方式
id: 10,
username: 'admin'
});
}, [])
const handleClick = () => {
dispatchs({ // 方法二的派发使用方式
key: 'user',
type: 'getUserAsync',
payload: {
id: 20,
username: 'admin2'
}
});
};
return (
<div>
user-id: {id}
<br/>
username: {username}
<br/>
<button onClick={handleClick}>修改</button>
</div>
)
}
dva 状态管理
也是个redux简化版,官方地址https://dvajs.com/。umi二次框架默认指定安装了这个。
这里也简单记录一下使用方式。
模板src/models/test.js:
import { getLists } from '@/api/search'
export default {
namespace: 'test', // 没有该属性就会自动取文件名
state: {
text: 'dva',
lists: []
},
// 同步
reducers: {
getLists(state, action){ // 第一个为默认state传入,第二个为调用该方法传入的参数
return {
...state,
lists: action.payload
}
}
},
// 异步
effects: {
*getListsAsync({payload}, {call, put}){ // call为异步操作,put为同步派发
const res = yield call(getLists, payload); // call传入异步函数,以及异步函数里的参数,以await的形式返回结果
yield put({
type: 'getLists',
payload: res.lists
})
}
}
}
在父级组件中使用:
import { connect } from 'dva';
class DvaDemo extends Component { // 需要中间层包裹
constructor(props) {
super(props);
}
render() {
return (
<div>
</div>
)
}
}
export default connect(({test})=>({ // 要哪个models文件,就存入对应文件名
test
}))(DvaDemo)
在子级组件中使用
mport React, { Component } from 'react';
export default class Lists extends Component {
constructor(props) {
super(props);
}
render() {
const { lists: {text, data} } = this.props; // 获取state
this.props.dispatch({ // 执行同步
type: 'test/getLists', // 第一个为namespace的属性值
payload: '参数'
})
this.props.dispatch({ // 执行异步
type: 'test/getDataAsync',
payload: '参数'
})
return (
<div>
</div>
)
}
}
MobX(网上最推荐)
这是网上最推荐的redux替代方案,官方地址:https://zh.mobx.js.org/installation.html
这个库的写法完全和redux不一样,而且他是可以通过直接修改state来改变其值的,和vuex很像。大家都认为它是未来,现在差就差在生态没redux那么丰富。
建议如果项目是很老的版本,已经难以升级,又不想用老版的redux,可以试试这个。
12-15~17
未来会持续补充…