一、父子组件表单域通信
1.1 受控表单域写法
新建Login.js
组件,写入:
import React, { Component } from 'react'
class Filed extends Component {
render() {
return <div style={{background: "yellow"}}>
<label>{this.props.label}</label>
<input value={this.props.value} type={this.props.type} onChange={(event) => {
this.props.onChangeEvt(event.target.value)
}}></input>
</div>
}
}
export default class Login extends Component {
state = {
name: localStorage.getItem('name') || '',
password: ''
}
render() {
return (
<div>
<h1>登录页面</h1>
<Filed label="用户名" type="text" onChangeEvt={(value) => {
console.log(value)
this.setState({
name: value
})
}} value={this.state.name}></Filed>
<Filed label="密码" type="password" onChangeEvt={(value) => {
console.log(value)
this.setState({
password: value
})
}} value={this.state.password}></Filed>
<button onClick={() => {
console.log(this.state,'请求后端登录接口')
}}>登录</button>
<button onClick={() => {
console.log(888)
this.setState({
name: '',
password: ''
})
}}>重置</button>
</div>
)
}
}
可以看到我们的Filed
组件是子组件,并且是受控的,可以看到在表单域的受控子组件中,这种写法是非常繁琐的。我们先看看效果:
1.2 ref版表单域组件
接下来我们将用ref
表单域写法,新建LoginRef.js
组件代码:
import React, { Component } from 'react'
class Filed extends Component {
state = {
value: ""
}
clear() {
this.setState({
value: ""
})
}
render() {
return <div style={{background: "yellow"}}>
<label>{this.props.label}</label>
<input type={this.props.type} onChange={(event) => {
this.setState({
value: event.target.value
})
}} value={this.state.value}></input>
</div>
}
}
export default class LoginRef extends Component {
name = React.createRef()
password = React.createRef()
state = {
name: '',
password: ''
}
render() {
return (
<div>
<h1>登录页面</h1>
<Filed label="用户名" type="text" ref={this.name}></Filed>
<Filed label="密码" type="password" ref={this.password}></Filed>
<button onClick={() => {
console.log(this.name.current.state.value, this.password.current.state.value, '请求登录接口')
}}>登录</button>
<button onClick={() => {
this.name.current.clear()
this.password.current.clear()
}}>重置</button>
</div>
)
}
}
可以看到我们将状态定义在了子组件中,并且清空方法也在子组件中,我们在父组件中直接用了 ref
来获取到值和重置表单,效果:
1.3 非父子通信方式
1、状态提升(中间人模式)
React
中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件上,在父组件上改变这个状态然后通过props
分发给子组件。
创建组件MiddleCp.js
写入代码:
import React, { Component } from 'react'
import axios from 'axios'
import '../css/middlecp.css'
export default class MiddleCp extends Component {
constructor() {
super()
this.state = {
filmList: [],
info: '',
}
axios.get('/data.json').then(res => {
console.log(res.data.data.films)
this.setState({
filmList: res.data.data.films
})
}, err => {
console.log(err)
})
}
render() {
return (
<div>
{
this.state.filmList.map(item => {
return <FilmItem key={item.filmId} {...item} onEvent={(value) => {
console.log('父组件接收', value)
this.setState({
info: value
})
}}></FilmItem>
})
}
<FilmDetail info={this.state.info}></FilmDetail>
</div>
)
}
}
class FilmItem extends Component {
render() {
let {name, poster, grade, synopsis} = this.props
return (
<div className="filmitem" onClick={() => {
this.props.onEvent(synopsis)
}}>
<img src={poster} alt={name}></img>
<h4>{name}</h4>
<div>观众评分:{grade}</div>
</div>
)
}
}
class FilmDetail extends Component {
render() {
return (
<div className="filmdetail">
{this.props.info}
</div>
)
}
}
middlecp.css
写入代码:
.filmitem img {
width: 100px;
float: left;
margin-right: 10px;
}
.filmitem {
overflow: hidden;
padding: 10px;
}
.filmdetail {
position: fixed;
right: 0;
top: 100px;
background-color: skyblue;
width: 300px;
height: 300px;
}
在public
文件夹下创建data.json
,写入数据。
效果如下:
完成了中间人传值模式。
1.4 非父子通信(订阅发布模式)
新建组件subscribe.js
写入代码:
import React, { Component } from 'react'
import axios from 'axios'
import '../css/middlecp.css'
// 调度中心
var bus = {
list: [],
// 订阅
subscribe(callback) {
this.list.push(callback)
},
// 发布
publish(text) {
this.list.forEach(callback => {
callback && callback(text)
})
}
}
export default class Subscribe extends Component {
constructor() {
super()
this.state = {
filmList: [],
}
axios.get('/data.json').then(res => {
console.log(res.data.data.films)
this.setState({
filmList: res.data.data.films
})
}, err => {
console.log(err)
})
}
render() {
return (
<div>
{
this.state.filmList.map(item => {
return <FilmItem key={item.filmId} {...item}></FilmItem>
})
}
<FilmDetail></FilmDetail>
</div>
)
}
}
class FilmItem extends Component {
render() {
let {name, poster, grade, synopsis} = this.props
return (
<div className="filmitem" onClick={() => {
// console.log(synopsis)
bus.publish(synopsis)
}}>
<img src={poster} alt={name}></img>
<h4>{name}</h4>
<div>观众评分:{grade}</div>
</div>
)
}
}
class FilmDetail extends Component {
constructor() {
super()
this.state = {
info: ""
}
bus.subscribe((info) => {
// console.log(info)
this.setState({
info: info
})
})
}
render() {
return (
<div className="filmdetail">
{this.state.info}
</div>
)
}
}
可以看到我们采用了发布订阅的模式,也完成了非父子组件通信。
1.5 非父子通信context方案
context
状态树传参。
创建context.js
组件,写入代码:
import React, { Component } from 'react'
import axios from 'axios'
import '../css/middlecp.css'
const GlobalContext = React.createContext(); // 创建context对象
export default class Context extends Component {
constructor() {
super()
this.state = {
filmList: [],
info: ""
}
axios.get('/data.json').then(res => {
console.log(res.data.data.films)
this.setState({
filmList: res.data.data.films
})
}, err => {
console.log(err)
})
}
render() {
return (
<GlobalContext.Provider value={{
info: this.state.info,
changeInfo: (value) => {
this.setState({
info: value
})
}
}}>
<div>
{
this.state.filmList.map(item => {
return <FilmItem key={item.filmId} {...item}></FilmItem>
})
}
<FilmDetail></FilmDetail>
</div>
</GlobalContext.Provider>
)
}
}
class FilmItem extends Component {
render() {
let { name, poster, grade, synopsis } = this.props
return (
<GlobalContext.Consumer>
{(value) => {
console.log(value)
return <div className="filmitem" onClick={() => {
value.changeInfo(synopsis)
}}>
<img src={poster} alt={name}></img>
<h4>{name}</h4>
<div>观众评分:{grade}</div>
</div>
}}
</GlobalContext.Consumer>
)
}
}
class FilmDetail extends Component {
render() {
return (
<GlobalContext.Consumer>
{
(value) => <div className="filmdetail">
{value.info}
</div>
}
</GlobalContext.Consumer>
)
}
}
可以看到我们借用了context
状态树来进行非父子同信,不管是怎样的嵌套方式,都可以进行通信。
在学习React的路上,如果你觉得本文对你有所帮助的话,那就请关注点赞评论三连吧,谢谢,你的肯定是我写博的另一个支持。