React 组件与通信
组件通信方式
| 方式 | 适用场景 |
|---|---|
| props | 父传子 |
| props 回调 | 子传父 |
| ref | 父调子方法 |
| Context | 跨层级 |
| children | 插槽式 |
| 状态管理(Redux) | 全局 |
父传子(props)
jsx
// 父组件
<Child title="标题" count={this.state.count} />
// 函数组件接收
function Child(props) {
return <h1>{props.title}</h1>
}
// 类组件接收
class Child extends Component {
render() {
return <h1>{this.props.title}</h1>
}
}// 父组件
<Child title="标题" count={this.state.count} />
// 函数组件接收
function Child(props) {
return <h1>{props.title}</h1>
}
// 类组件接收
class Child extends Component {
render() {
return <h1>{this.props.title}</h1>
}
}props 类型验证
jsx
import PropTypes from 'prop-types'
class MyComponent extends Component {
static propTypes = {
title: PropTypes.string.isRequired,
count: PropTypes.number,
user: PropTypes.object,
onClick: PropTypes.func,
children: PropTypes.element
}
static defaultProps = {
count: 0
}
}import PropTypes from 'prop-types'
class MyComponent extends Component {
static propTypes = {
title: PropTypes.string.isRequired,
count: PropTypes.number,
user: PropTypes.object,
onClick: PropTypes.func,
children: PropTypes.element
}
static defaultProps = {
count: 0
}
}子传父(props 回调)
jsx
// 子组件
function Child(props) {
return <button onClick={() => props.onChange('data')}>发送</button>
}
// 父组件
class Parent extends Component {
handleData = (data) => {
console.log('收到子组件数据:', data)
}
render() {
return <Child onChange={this.handleData} />
}
}// 子组件
function Child(props) {
return <button onClick={() => props.onChange('data')}>发送</button>
}
// 父组件
class Parent extends Component {
handleData = (data) => {
console.log('收到子组件数据:', data)
}
render() {
return <Child onChange={this.handleData} />
}
}父调子方法(ref)
类组件
jsx
// 子组件
class Child extends Component {
myMethod = () => console.log('子组件方法')
render() {
return <div>Child</div>
}
}
// 父组件
class Parent extends Component {
childRef = React.createRef()
handleClick = () => {
this.childRef.current.myMethod()
}
render() {
return (
<>
<Child ref={this.childRef} />
<button onClick={this.handleClick}>调用子组件方法</button>
</>
)
}
}// 子组件
class Child extends Component {
myMethod = () => console.log('子组件方法')
render() {
return <div>Child</div>
}
}
// 父组件
class Parent extends Component {
childRef = React.createRef()
handleClick = () => {
this.childRef.current.myMethod()
}
render() {
return (
<>
<Child ref={this.childRef} />
<button onClick={this.handleClick}>调用子组件方法</button>
</>
)
}
}函数组件(useImperativeHandle)
jsx
// 子组件
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
myMethod: () => console.log('子组件方法')
}))
return <div>Child</div>
})
// 父组件
function Parent() {
const childRef = useRef(null)
return <Child ref={childRef} />
}// 子组件
const Child = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
myMethod: () => console.log('子组件方法')
}))
return <div>Child</div>
})
// 父组件
function Parent() {
const childRef = useRef(null)
return <Child ref={childRef} />
}props.children
组件标签中间的内容:
jsx
<Card>这是卡片内容</Card>
function Card(props) {
return <div className="card">{props.children}</div>
}<Card>这是卡片内容</Card>
function Card(props) {
return <div className="card">{props.children}</div>
}children 作为函数(作用域插槽)
jsx
function List(props) {
return <ul>{props.children}</ul>
}
function App() {
return (
<List>
{(item) => <li key={item.id}>{item.name}</li>}
</List>
)
}function List(props) {
return <ul>{props.children}</ul>
}
function App() {
return (
<List>
{(item) => <li key={item.id}>{item.name}</li>}
</List>
)
}修改 children
children 只读,需先克隆再修改:
jsx
function RadioGroup(props) {
return (
<div>
{React.Children.map(props.children, (child) =>
React.cloneElement(child, { name: props.name })
)}
</div>
)
}function RadioGroup(props) {
return (
<div>
{React.Children.map(props.children, (child) =>
React.cloneElement(child, { name: props.name })
)}
</div>
)
}React.Children API
jsx
React.Children.map(children, fn) // 遍历
React.Children.forEach(children, fn) // 遍历(无返回值)
React.Children.count(children) // 计数
React.Children.only(children) // 验证唯一子节点
React.Children.toArray(children) // 转数组React.Children.map(children, fn) // 遍历
React.Children.forEach(children, fn) // 遍历(无返回值)
React.Children.count(children) // 计数
React.Children.only(children) // 验证唯一子节点
React.Children.toArray(children) // 转数组Context(跨层级通信)
新版 createContext
jsx
import { createContext } from 'react'
const ThemeContext = createContext('light')
// 父组件 — 提供数据
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
)
}
// 子组件 — 消费数据
function Child() {
const theme = useContext(ThemeContext)
return <div>当前主题:{theme}</div>
}
// 或使用 Consumer
function Child() {
return (
<ThemeContext.Consumer>
{(value) => <div>当前主题:{value}</div>}
</ThemeContext.Consumer>
)
}import { createContext } from 'react'
const ThemeContext = createContext('light')
// 父组件 — 提供数据
function App() {
return (
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
)
}
// 子组件 — 消费数据
function Child() {
const theme = useContext(ThemeContext)
return <div>当前主题:{theme}</div>
}
// 或使用 Consumer
function Child() {
return (
<ThemeContext.Consumer>
{(value) => <div>当前主题:{value}</div>}
</ThemeContext.Consumer>
)
}旧版(类组件)
jsx
import PropTypes from 'prop-types'
// 父组件
class Parent extends Component {
static childContextTypes = {
color: PropTypes.string
}
getChildContext() {
return { color: 'red' }
}
render() {
return <Child />
}
}
// 子组件
class Child extends Component {
static contextTypes = {
color: PropTypes.string
}
render() {
return <div style={{ color: this.context.color }}>内容</div>
}
}import PropTypes from 'prop-types'
// 父组件
class Parent extends Component {
static childContextTypes = {
color: PropTypes.string
}
getChildContext() {
return { color: 'red' }
}
render() {
return <Child />
}
}
// 子组件
class Child extends Component {
static contextTypes = {
color: PropTypes.string
}
render() {
return <div style={{ color: this.context.color }}>内容</div>
}
}传入组件 vs 传入组件名
jsx
// 传入组件实例(可传 state)
<Son extra={<Branch count={this.state.count} />} />
// 传入组件类(由子组件实例化)
<Son Extra={Branch} />
// 子组件中渲染
function Son(props) {
const Extra = props.Extra
return (
<div>
{props.extra}
<Extra />
</div>
)
}// 传入组件实例(可传 state)
<Son extra={<Branch count={this.state.count} />} />
// 传入组件类(由子组件实例化)
<Son Extra={Branch} />
// 子组件中渲染
function Son(props) {
const Extra = props.Extra
return (
<div>
{props.extra}
<Extra />
</div>
)
}
Cao Note