父子组件通信
类组件
通过props
import React, { Component } from 'react'
// 父组件
export default class App extends Component {
state = {
num: 1
}
// 父组件中的方法执行的事情
add=()=>{
this.setState({
num:this.state.num+1
})
}
render() {
return (
<div>
{/* 通过 */}
<div>子组件中触发的加一:{this.state.num}</div>
{/* 通过属性名={传递的值} 来向子组件传值 通过方法名={方法} 来向子组件传递一个方法*/}
<App2 num={this.state.num} add={this.add}/>
</div>
)
}
}
// 子组件
class App2 extends Component {
// 子组件通过自身的方法,像父组件提交了一个功能,触发父组件的方法
add = () => {
// 触发父组件的传来的方法
this.props.add()
}
render() {
return (
<div>
<div>
{/* 通过this.props.属性名 来接受父组件传来的值 */}
父组件传来的数值:{this.props.num}
</div>
<button onClick={this.add}>加一</button>
</div>
)
}
}
函数组件
import React,{useState} from 'react'
export default function App() {
const [num,setNum] = useState(0)//定义一个变量num
const add=()=>{
setNum(num+1)//修改num,使之加一
}
return (
<div>
<div>子传父更改的值:{num}</div>
{/* 通过属性名={传递的值} 来向子组件传值 通过方法名={方法} 来向子组件传递一个方法*/}
<App2 num={num} add={add}/>
</div>
)
}
function App2(props) {
const add = () => {
// 触发父组件的传来的方法
props.add()
}
return (
<div>
<div>
{/* 通过props.属性名 来接受父组件传来的值 */}
父组件传来的数值:{props.num}
</div>
<button onClick={add}>加一</button>
</div>
)
}
非父子通信
状态提升
import React, { Component } from 'react'
/*
儿子2 想接受儿子1里面的数据,并实现加1,
*/
// 中间人
export default class App11 extends Component {
state={
acceptNum:0//用来接受儿子1传来的值
}
tranNum = (sub1Value) => {
console.log("能接受到儿子1传来的值吗?",sub1Value);
// 将儿子1传来的值放到父亲身上
this.setState({
acceptNum:sub1Value
})
}
tranNum2 = (sub2Value) => {
console.log("能接受到儿子2传来的值吗?",sub2Value);
// 将儿子1传来的值放到父亲身上
this.setState({
acceptNum:sub2Value
})
}
render() {
return (
<div>
<Sub1 tranNum={this.tranNum} />
<div>{this.state.acceptNum}</div>
<hr />
{/* 将儿子1传来的值,通过父亲转接,传给儿子2 */}
<Sub2 sub1Num={this.state.acceptNum} tranNum2={this.tranNum2}/>
<hr />
{/* 儿子2更改后的的值传回给儿子1 */}
<Sub1 sub2Num={this.state.acceptNum} />
</div>
)
}
}
// 儿子1
class Sub1 extends Component {
state = {
num: 1
}
// 儿子1向父亲传递num里面的值
transferFn = (num) => {
this.props.tranNum(num)
}
render() {
return (
<div>
<button onClick={this.transferFn.bind(this, this.state.num)}>Sub1向父亲传值</button>
<div>拿到儿子2通过加一后传来的值{this.props.sub2Num}</div>
</div>
)
}
}
// 儿子2
class Sub2 extends Component {
// 儿子2修改了儿子1的值,并给父亲发了个指令
addFn=(sub1Num)=>{
var sub2Num = sub1Num+1
this.props.tranNum2(sub2Num)
}
render() {
return (
<div>
<div>儿子2经过父亲,拿到儿子1传来的值:{this.props.sub1Num}</div>
<button onClick={this.addFn.bind(this,this.props.sub1Num)}>加一</button>
</div>
)
}
}
不利于开发者维护
发布订阅模式
实际开发不会使用原生的发布订阅者模式,会用redux(基于发布订阅者封装好的)方法来实现这种非父子通信的模式
import React, { Component } from 'react'
export default class App extends Component {
render() {
return (
<div>发布订阅</div>
)
}
}
var bus = {
list:[],
// 发布
subscribe(callback) {
console.log(callback);
this.list.push(callback)
},
// 订阅
publish(text) {//text传的参数
// 遍历所有的list,将回调函数执行
this.list.forEach(callback=>{
callback&&callback(text)
})
}
}
// 订阅者
bus.subscribe((value)=>{//value接受发布者传来的参数
console.log('111',value);
})
bus.subscribe(()=>{
console.log("121");
})
bus.publish("传进去的参数1")
bus.publish("传进去的参数2")
context状态树
在新的context API中 React提供了一个createContext的方法,该方法返回了一个包含Provider,Consumer对象,需要注意的是:提供者一定要是消费者的某一层父级
(1)先定义全局context对象
(2)根组件引入GlobalContext,并使用GlobalContext.Provider(生产者)
(3)类组件中,任意组件引入GlobalContext并调用Context,使用GlobalContext.Consumer(消费者),返回一个函数,并传入value。
value.变量 获取数据
value.方法 获取方法
{
// 必须要是一个箭头函数
(value) => {
return (//必须要return出去
<div>
<div>生产者提供的值:{value.num}</div>
<button onClick={this.addFn.bind(this, value)}>加一</button>
</div>
)
}
}
(4)函数式组件中,通过使用hooks提供的useContext来获取参数。
value.变量 获取数据
value.方法 获取方法
const value = useContext(GlobalContext)
console.log(value);
const addFn = (value) => {
value.changeNum()
}
return (
<div>
<div>生产者提供的值:{value.num}</div>
<button onClick={() => addFn(value)}>加一</button>
</div>
)
实现代码:
类组件
import React, { Component, createContext } from 'react'
/*
1、实现过程
(1)先定义全局context对象
(2)根组件引入GlobalContext,并使用GlobalContext.Provider(生产者)
(3)任意组件引入GlobalContext并调用Context,使用GlobalContext.Consumer(消费者)
*/
// 1、全局定义context对象
const GlobalContext = React.createContext()
// 生产者
export default class Context extends Component {
state = {
num: 1
}
render() {
return (
// 一定要为父标签,作为唯一的根标签
<GlobalContext.Provider value={{
num: this.state.num,//传递过去的值
changeNum: (value) => {
this.setState({
num: this.state.num + 1
})
}
}}>
<Consumer />
</GlobalContext.Provider>
)
}
}
// 消费者
class Consumer extends Component {
// 消费者向生产者发送的指令
addFn = (value) => {
// 执行生产者中的方法
value.changeNum()
}
render() {
return (
<GlobalContext.Consumer>
{
// 必须要是一个箭头函数
(value) => {
return (//必须要return出去
<div>
<div>生产者提供的值:{value.num}</div>
<button onClick={this.addFn.bind(this, value)}>加一</button>
</div>
)
}
}
</GlobalContext.Consumer>
)
}
}
函数组件-useContext()
没有引入useContext的写法
import React,{useState} from 'react'
// 1、全局定义context对象
const GlobalContext = React.createContext()
export default function App() {
const [num,setNum] = useState(0)
return (
// 一定要为父标签,作为唯一的根标签
<GlobalContext.Provider value={{
num: num,//传递过去的值
changeNum: (value) => {
setNum(num + 1)
}
}}>
<Sub1 />
</GlobalContext.Provider>
)
}
function Sub1(){
const addFn=(value)=>{
value.changeNum()
}
return (
<GlobalContext.Consumer>
{
// 必须要是一个箭头函数
(value) => {
console.log(value);
return (//必须要return出去
<div>
<div>生产者提供的值:{value.num}</div>
<button onClick={()=>addFn(value)}>加一</button>
</div>
)
}
}
</GlobalContext.Consumer>
)
}
引入useContext的写法
import React, { useState, useContext } from 'react'
// 1、全局定义context对象
const GlobalContext = React.createContext()
export default function App() {
const [num, setNum] = useState(0)
return (
// 一定要为父标签,作为唯一的根标签
<GlobalContext.Provider value={{
num: num,//传递过去的值
changeNum: (value) => {
setNum(num + 1)
}
}}>
<Sub1 />
</GlobalContext.Provider>
)
}
function Sub1() {
const value = useContext(GlobalContext)
console.log(value);
const addFn = (value) => {
value.changeNum()
}
return (
<div>
<div>生产者提供的值:{value.num}</div>
<button onClick={() => addFn(value)}>加一</button>
</div>
)
}
注意 :
(1)提供者一定要是消费者的某一层父级
(2)消费者的结构必须必须要是一个箭头函数,而且必须要return出去
(3)函数组件的实现方式也是如此,只是存储的变量需要用useState
Redux状态管理
Redux术语
(1)Action
描述应用程序中发生了什么事件,有两个参数
type:String,给action起一个描述性的名字,通常的格式“域/事件名称”,域:是action所属的特征或类别;事件名称:具体发生的事情
action:可以有其他字段,其中包含有关发生的事情的附加信息,平常将该信息放在名为payload的字段中
// 创建action对象
const action = {
type: "changeInputValue", // type属性是必须要写的,用于校验
value: e.target.value, // value代表要修改为什么值
}
(2)reducer
是一个函数,接收当前的state(初始值)和一个action对象,必要是决定如何更新状态,并返回新状态。可以视为一个事件监听器,根据接受到的action类型处理事件
state:指的是原始仓库里的状态
action:指的是action新传递的状态
函数内部的逻辑通常遵循的步骤:检查reducer是否关心action,如果关心则赋值state,使用新值更新state副本,然后返回新state;否则返回原来的state不变
//初始默认值
const initialState = {
value: 0
}
//state:指的是原始仓库里的状态
//action:指的是action新传递的状态
function counterReducer(state = initialState, action) {
// 检查 reducer 是否关心这个
action if (action.type === 'counter/increment') {
// 如果是,复制 `state`
return {
...state, // 使用新值更新 state 副本
value: state.value + 1
}
}
// 返回原来的 state 不变
return state
}
通常用switch来遍历
(3)store
相当于一个仓库,当前redux应用的state存在与一个名为store的对象中
通过传入reducer来创建
通过getState()来获取仓库的内容,返回当前状态值
// 引入createStore对象
import { createStore } from 'redux' // 引入reducer
import reducer from './reducer'
const store = createStore( reducer );
export default store; //获取当前状态值
// 引入store
import store from './store' var xxx = store.getState()
(4)dispatch
用来更新state的唯一方法是调用store.dispatch()并传入一个action对象,store 将执行所有 reducer 函数并计算出更新后的 state,调用 getState() 可以获取新 state。
store.dispatch({ type: 'counter/increment' })
console.log(store.getState()) // {value: 1}