React学习分享(四)

十、表单中的受控组件与非受控组件

1、非受控组件

React要编写一个非受控组件,可以使用ref来从DOM节点中获取表单数据,就是非受控组件。
例如:

import React, {
    
     Component } from 'react'
export default class App extends Component {
    
    
    mytext = React.createRef()
    render() {
    
    
        return (
            <div>
                <input type="text" ref={
    
    this.mytext} defaultValue="asdad" />
                <button onClick={
    
    
                    () => {
    
     console.log(this.mytext.current.value); }
                }>获取Value</button>
                <button onClick={
    
    () => {
    
    
                    this.mytext.current.value = ""
                }}>重置</button>
            </div >
        )
    }
}

因为非受控组件将真实数据储存在DOM节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。

在 React 渲染生命周期时,表单元素上的value将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望React能赋予组件一个初始值,但是不去控制后续的更新。 在这种情况下, 你可以指定一个defaultValue属性,而不是 value。(defaultValue是在非受控下使用,而value是在受控的时候使用)

同样,checkbox radio支持defaultChecked,select和textarea支持defaultValue

1、受控组件

import React, { Component } from 'react'
export default class App extends Component {
    state = {
        mytext: "123"
    }
    render() {
        return (
            <div>
                <input type="text" value={this.state.mytext} onChange={(e) => {
                    this.setState({
                        mytext: e.target.value
                    })
                }} />
                <button onClick={() => {
                    console.log(this.state.mytext);
                }}>获取Value</button>
                <button onClick={() => {
                    this.setState({
                        mytext: ""
                    })
                }}>重置</button>
            </div>
        )
    }
}

原理和vue的双向数据绑定类似(v-model)

由于在表单元素上设置了value属性,因此显示的值将始终为this.state.value ,这使得React的state成为唯一数据源。由于handlechange在每次按键时都会执行并更新React的state,因此显示的值将随着用户输入而更新。

对于受控组件来说,输入的值始终由React的state驱动。你也可以将value 传递给其他UI元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。

注意: 另一种说法(广义范围的说法),React组件的数据渲染是否被调用者传递的props完全控制,控制则为受控组件,否则非受控组件。

十一、组件间通信

父子组件内通信

  1. 传递数据(父传子)与传递方法(子传父)

父组件:

import React, {
    
     Component } from 'react'
import './css/01-maizuo.css'
import Home from "./compontents/home"
import Cinema from "./compontents/cinema"
import My from "./compontents/My"
import Tabbar from './compontents/Tabbar'
import Navbar from './compontents/Navbar'
export default class APP extends Component {
    
    
    state = {
    
    
        list: [
            {
    
    
                id: 0,
                text: "首页"
            },
            {
    
    
                id: 1,
                text: "电影"
            },
            {
    
    
                id: 2,
                text: "我的"
            }
        ],
        current: 0
    }
    switch() {
    
    
        switch (this.state.current) {
    
    
            case 0:
                return <Home></Home>
            case 1:
                return <Cinema></Cinema>
            case 2:
                return <My></My>
            default:
                return null
        }
    }
    render() {
    
    
        return (
            <div>
                <Navbar event={
    
    this.tarrget} />
                {
    
    
                    //表达式--支持函数表达式
                    this.switch()
                }
                <Tabbar current={
    
    this.state.current} list={
    
    this.state.list} event={
    
    this.tarrget}></Tabbar>
            </div>
        )
    }
    tarrget = (index) => {
    
    
        this.setState({
    
    
            current: index
        })
    }
}

子组件Tabbar:

import React, {
    
     Component } from 'react'
export default class Tabbar extends Component {
    
    
    render() {
    
    
        return (
            <div>
                <div>
                    <ul>
                        {
    
    
                            this.props.list.map((item, index) =>
                                <li key={
    
    item.id} className={
    
    this.props.current === index ? 'active' : ''} onClick={
    
    () => this.click(index)}>{
    
    item.text}</li>)
                        }
                    </ul>
                </div>
            </div>
        )
    }
    click = (index) => {
    
    
        this.props.event(index)
    }
}

子组件Navbar:

import React, {
    
     Component } from 'react'
export default class Navbar extends Component {
    
    
    render() {
    
    
        return (
            <div>
                <div style={
    
    {
    
     background: "yellow", textAlign: "center", overflow: "hidden" }}>
                    <button style={
    
    {
    
     float: "left" }}>back</button>
                    <span>卖座电影</span>
                    <button style={
    
    {
    
     float: "right" }} onClick={
    
    () => this.goHome(0)}>home</button>
                </div>
            </div>
        )
    }
    goHome = (index) => {
    
    
        this.props.event(index)
    }
}

这里是父组件向子组件传递属性,子组件通过回调函数返回返回值,采用的是受控的方式对子组件进行控制,意思是在子组件中不过多的使用state状态,而是通过父组件的属性进行控制,减少组件内的状态。

2.ref标记 (父组件拿到子组件的引用,从而调用子组件的方法)
代码:

import React, {
    
     Component } from 'react'
export default class App extends Component {
    
    
    username = React.createRef()
    password = React.createRef()
    render() {
    
    
        return (
            <div>
                <Field label="用户名" type="text" ref={
    
    this.username}></Field>
                <Field label="密码" type="password" ref={
    
    this.password}></Field>
                <button onClick={
    
    this.login}>登录</button>
                <button onClick={
    
    this.set}>设置登录名</button>
                <button onClick={
    
    () => {
    
    
                    this.password.current.clear()
                    this.username.current.clear()
                }}>取消</button>
            </div>
        )
    }
    login = () => {
    
    
        console.log(this.username.current.state.value);
        console.log(this.password.current.state.value);
    }
    set = () => {
    
    
        this.username.current.setValue(123)
    }
}
class Field extends Component {
    
    
    state = {
    
    
        value: ""
    }
    clear = () => {
    
    
        this.setState({
    
    
            value: ""
        })
    }
    setValue = (value) => {
    
    
        this.setState({
    
    
            value: value
        })
    }
    render() {
    
    
        return (
            <div>
                <label>{
    
    this.props.label}</label>
                <input onChange={
    
    
                    (e) => {
    
    
                        this.setValue(e.target.value)
                    }
                } value={
    
    this.state.value} type={
    
    this.props.type} />
            </div>
        )
    }
}

这里我们通过对子组件绑定ref 获取组件中的状态和方法,在父组件中进行对状态和方法的改变。类似于面向对象的思想。

非父子组件内通信

1.状态提升(中间人模式)

React中的状态提升概括来说,就是将多个组件需要共享的状态提升到它们最近的父组件 上.在父组件上改变这个状态然后通过props分发给子组件。总之就是,我们在处理两个兄弟组件时,可以通过回调的方式兄弟1组件把信息传递到父组件内,再通过props父组件传递到兄弟2组件,就可以收到信息了。

  1. 发布订阅模式实现
    首先我们先看这部分代码(发布订阅的核心代码):
//调度中心
var bus = {
    
    
    list:[],
    //订阅
    subscribe(callback){
    
    
        // console.log(callback)
        this.list.push(callback)
    },
    //发布
    publish(text){
    
    
        //遍历所有的list, 将回调函数执行
        // console.log(this.list)
        this.list.forEach(callback=>{
    
    
            callback && callback(text)
        })
    }
}

解读代码:我们在一个组件中订阅一个消息通过回调函数的方式传递,把每次订阅的回调放在一个全局变量数组中,在发布中,我们去遍历这个数组,将里面的回调函数进行执行。
案例:
js:

import React, {
    
     Component } from 'react'
import axios from 'axios'
//调度中心
var bus = {
    
    
    list:[],
    //订阅
    subscribe(callback){
    
    
        // console.log(callback)
        this.list.push(callback)
    },
    //发布
    publish(text){
    
    
        //遍历所有的list, 将回调函数执行
        // console.log(this.list)
        this.list.forEach(callback=>{
    
    
            callback && callback(text)
        })
    }
}
export default class App extends Component {
    
    
    constructor(){
    
    
        super()
        this.state = {
    
    
            filmList:[],
        }
        axios.get(`/test.json`).then(res=>{
    
    
            console.log(res.data.data.films)
            this.setState({
    
    
                filmList:res.data.data.films
            })
        })
    }
    render() {
    
    
        return (
            <div>
                {
    
    /* {this.state.info} */}
                {
    
    
                    this.state.filmList.map(item=>
                        <FilmItem key={
    
    item.filmId} {
    
    ...item} ></FilmItem>    
                    )
                }
                <FilmDetail ></FilmDetail>
            </div>
        )
    }
}
/*受控组件*/
class FilmItem extends Component{
    
    
    render(){
    
    
        // console.log(this.props)
        let {
    
    name, poster,grade,synopsis}  = this.props
        return <div className="filmitem" onClick={
    
    ()=>{
    
    
            // console.log(synopsis)
            bus.publish(synopsis)
        }}>
            <img src={
    
    poster} alt={
    
    name}/>
            <h4>{
    
    name}</h4>
            <div>观众评分:{
    
    grade}</div>
        </div>
    }
}
class FilmDetail extends Component{
    
    
    constructor(){
    
    
        super()
        this.state = {
    
    
            info:""
        }
        bus.subscribe((info)=>{
    
    
            console.log("我再filmDetail中定义",info)
            this.setState({
    
    
                info:info
            })
        })
    }
    render(){
    
    
        return <div className="filmdetail">
            {
    
    this.state.info}
        </div>
    }
}
  1. context状态树传参
import React, {
    
     Component } from 'react'
import axios from 'axios'
const GlobalContext  = React.createContext() //创建context对象
export default class App extends Component {
    
    
    constructor(){
    
    
        super()
        this.state = {
    
    
            filmList:[],
            info:""
        }
        axios.get(`/test.json`).then(res=>{
    
    
            console.log(res.data.data.films)
            this.setState({
    
    
                filmList:res.data.data.films
            })
        })
    }
    render() {
    
    
        return (
            <GlobalContext.Provider value={
    
    {
    
    
                call:"打电话",
                sms:"短信",
                info:this.state.info,
                changeInfo:(value)=>{
    
    
                    this.setState( {
    
    
                        info:value
                    })
                }
            }}>
                <div>
                    {
    
    /* {this.state.info} */}
                    {
    
    
                        this.state.filmList.map(item=>
                            <FilmItem key={
    
    item.filmId} {
    
    ...item} ></FilmItem>    
                        )
                    }
                    <FilmDetail ></FilmDetail>
                </div>
            </GlobalContext.Provider>
        )
    }
}
/*受控组件*/
class FilmItem extends Component{
    
    
    render(){
    
    
        // console.log(this.props)
        let {
    
    name, poster,grade,synopsis}  = this.props
        return (
        <GlobalContext.Consumer>
           {
    
    
                (value)=>{
    
    
                    console.log(value)
                   
                    return <div className="filmitem" onClick={
    
    ()=>{
    
    
                        console.log(synopsis)
                        // this.props.onEvent(synopsis)
                        // value.info = "2222222"
                        // console.log(value)
                        value.changeInfo(synopsis)
                    }}>
                        <img src={
    
    poster} alt={
    
    name}/>
                        <h4>{
    
    name}</h4>
                        <div>观众评分:{
    
    grade}</div>
                    </div>
                }
           }
        </GlobalContext.Consumer>
        )    
    }
}
class FilmDetail extends Component{
    
    
    render(){
    
    
        return (
            <GlobalContext.Consumer>
                {
    
    
                    (value)=><div className="filmdetail">
                        detail-{
    
    value.info}
                    </div>
                }
            </GlobalContext.Consumer>
        )
    }
}

注意:GlobalContext.Consumer内必须是回调函数,通过context方法改变根组件状态

context优缺点: 优点:跨组件访问数据 缺点:react组件树种某个上级组件shouldComponetUpdate 返回false,当context更新时,不 会引起下级组件更新

十二、插槽

import React, {
    
     Component } from 'react'
class Child extends Component{
    
    
    render(){
    
    
        return <div>
            child
            {
    
    /* 插槽 vue slot,具名插槽 */}
            {
    
    this.props.children[2]}
            {
    
    this.props.children[1]}
            {
    
    this.props.children[0]}
        </div>
    }
}
class Swiper extends Component{
    
    
    render(){
    
    
        return <div>
            {
    
    this.props.children}
        </div>
    }
}
export default class App extends Component {
    
    
    render() {
    
    
        return (
            <div>
                <Swiper>
                    <div>111111</div>
                    <div>222222</div>
                    <div>333333</div>
                </Swiper>
                <Child>
                    <div>11111111</div>
                    <div>22222222</div>
                    <div>33333333</div>
                </Child>
            </div>
        )
    }
}

这里通过this.props.children来获取写在组件里面的东西
如果里面的东西想要获取多个div,也可以写成数据this.props.children[0],this.props.children[1]这样来得到,如果不写成数组this.props.children,则是获得到全部的div。
作用:

  1. 为了复用
  2. 一定程度减少父子通信。

猜你喜欢

转载自blog.csdn.net/weixin_46824709/article/details/126582864
今日推荐