1. 父子间传值
1.1 使用props实现父子间传值
概述:
-
父传子:通过自定义属性向子组件传值,值可以是任何类型
-
子传父:通过父组件把方法传给子组件,子组件调用传过去的方法完成传值
注意:
- 定义方法时尽可能的去使用箭头函数定义
- 调用方法时,尽可能的写小括号
使用:
父组件:
import React, { Component } from 'react'
import Child from './pages/Child'
class App extends Component {
state = {
num: 1
}
// 成员方法 箭头函数来定义,这样就没有this指向问题
setNum = (n = 1) => this.setState(state => ({ num: state.num + n }))
render() {
let { num } = this.state
return (
<div>
<Child num={num} setNum={this.setNum} />
</div>
)
}
}
export default App
子组件:
import React, { Component } from 'react'
class Child extends Component {
addNum = (evt, n = 1) => {
n = n * 10
this.props.setNum(n)
}
render() {
let { num, setNum } = this.props
return (
<div>
<h3>Child组件 -- {num}</h3>
{/* 子去修改父传过来的数据,props它是单向数据流,子不能直接去修改 */}
{/* 调用时,尽可能的写小括号 */}
<button onClick={() => setNum(2)}>+++++</button>
<button onClick={this.addNum}>+++++</button>
</div>
)
}
}
export default Child
1.2 使用ref实现父子间传值
概述:
父组件利用 ref 对子组件做标记,通过调用子组件的方法以更改子组件的状态,也可以调用子组件的方法(说明:ref 获取当前组件对象,只针对的是使用类创建的组件才可以用此方案,类有实例,而函数没有实例,默认只针对于类组件)
使用:
父组件:
import React, { Component, createRef } from 'react'
import Child from './pages/Child'
class App extends Component {
state = {
title: '',
msg: '父中的数据传给子' + Date.now()
}
childRef = createRef()
render() {
return (
<div>
{/*
父子间传值:通过ref引用对象完成对于【类组件】之间通信
this.childRef.current 得到当前Child组件的实例对象
*/}
<h3>{this.state.title}</h3>
<hr />
<Child ref={this.childRef} />
<button
onClick={() => {
// 子中的数据在父中使用
this.setState({
title: this.childRef.current.state.title
})
// this.childRef.current.setState({ title: this.state.msg })
this.childRef.current.setTitle(this.state.msg)
}}
>
父组件中修改子组件中的数据
</button>
</div>
)
}
}
export default App
子组件:
import React, { Component } from 'react'
class Child extends Component {
state = {
title: '我是子组件'
}
setTitle = title => this.setState({ title })
render() {
return (
<div>
<h3>{this.state.title}</h3>
</div>
)
}
}
export default Child
2. 状态提升
概述:
此方案用来解决兄弟组件间的传值问题,就是把共用的状态信息提升到父组件状态中。
使用:
import React, { Component } from 'react'
class Child1 extends Component {
render() {
let { setMsg } = this.props
return (
<div>
Child1<input type="text" onChange={e => setMsg(e.target.value)} />
</div>
)
}
}
class Child2 extends Component {
render() {
let { msg } = this.props
return (
<div>
<h3>Child2 -- {msg}</h3>
</div>
)
}
}
class App extends Component {
state = {
msg: ''
}
setMsg = msg => this.setState({ msg })
render() {
return (
<div>
<h1>App -- {this.state.msg}</h1>
<Child1 setMsg={this.setMsg} />
<Child2 msg={this.state.msg} />
</div>
)
}
}
export default App
3. 跨组件通信
概述:
在 react 中没有类似 vue 中的事件总线来解决这个问题。在实际的项目中,当需要组件间跨级访问信息时,如果还使用组件层层传递 props,此时代码显得不那么优雅,甚至有些冗余。在 react 中,我们还可以使用 context 来实现跨级父子组件间的通信。
Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。
在React的 Context 中,数据我们可当成商品,发布数据的组件会用 provider 身份(卖方),接收数据的组件使用 consumer 身份(买方)
使用:
appContext(创建Context对象):
// context一个项目中可以有N个,但是一般情况下我们使用它,都是把它放在App组件中,一个项目一个
// context实现在跨组件通信,一般用于自定义组件所用
// context绑定消费是一定是一个对象
import { createContext } from 'react'
// Provider,Consumer 都是组件
// Provider 生产数据的组件
// Consumer 消费的组件
// 导出写法1
// const ctx = createContext()
// export const { Provider, Consumer } = ctx
// export default ctx
// 导出写法2
const ctx = createContext()
const { Provider, Consumer } = ctx
export {
ctx as default,//默认导出
Provider,//按需导出
Consumer//按需导出
}
父组件(发布消息):
import React, { Component } from 'react'
import Child from './pages/Child'
// 在App组件中生产数据,这样下面的子孙组件就可以调用此context对象来消费数据
import { Provider } from './context/appContext'
class App extends Component {
state = {
name: '张三',
setName: name => this.setState({ name })
}
render() {
return (
<div>
{/* value属性就是生产的数据源 */}
<Provider value={this.state}>
<Child />
</Provider>
</div>
)
}
}
export default App
子组件(组件消费):
import React, { Component } from 'react'
// 假设现在当前组件是App组件下面的很深的子孙组件
import ctx, { Consumer } from '../../context/appContext'
class Child extends Component {
// 类组件中context还可以换一个方案来消费,定义好后,就可以通过成员属性 this.context来获取数据
static contextType = ctx
render() {
return (
<div>
{/* 消费方案1 */}
{/* <Consumer>{state => <h3>{state.name}</h3>}</Consumer> */}
{/* 消费方案2(常用方案) */}
<h3>{this.context.name}</h3>
<button
onClick={() => {
// 这里更改的是父组件中数据源的数据,父组件一改,父组件下所有的子孙组件都会更新
this.context.setName(Date.now())
}}
>
++++
</button>
</div>
)
}
}
export default Child
4. 状态提升案例:toDoList
父组件(App.jsx):
import React, { Component } from 'react'
import Todo from './todo'
import './app.css'
class App extends Component {
render() {
return (
<div>
<h3 className="app-title">App组件</h3>
<Todo />
</div>
)
}
}
export default App
子组件(index.jsx):
import React, { Component } from 'react'
import Form from './Form'
import List from './List'
class Todo extends Component {
state = {
todos: []
}
addTodo = todo => this.setState(state => ({ todos: state.todos.concat(todo) }))
render() {
let { todos } = this.state
return (
<div>
<Form addTodo={this.addTodo} />
<List todos={todos} />
</div>
)
}
}
export default Todo
子组件(form.jsx):
import React, { Component } from 'react'
class Form extends Component {
onEnter = e => {
if (e.key === 'Enter') {
let value = e.target.value.trim()
e.target.value = ''
this.props.addTodo({
id: Date.now(),
title: value,
done: false
})
}
}
render() {
return (
<div>
<input type="text" onKeyUp={this.onEnter} />
</div>
)
}
}
export default Form
子组件(list.jsx):
import React, { Component } from 'react'
import style from './style.module.css'
class List extends Component {
render() {
let { todos } = this.props
return (
<div>
<ul>
{todos.map(({ id, title, done }) => (
<li key={id}>
<span className={done ? style.title : ''}>{title}</span>
<span>删除</span>
</li>
))}
</ul>
</div>
)
}
}
export default List