React 扩展

一、setState

setState更新状态的2种写法

(1)setState(stateChange,,[callback])------对象式的setState

    stateChange为状态改变对象(该对象可以体现出状态的更改)

    callback是可选的回调函数,它在状态更新完毕,界面也更新后(render调用后)才被调用

(2)setState(updater, [callback])------函数式的setState

    updater为返回stateChange对象的函数

    updater可以接收到state和props

    callback是可选的回调函数,它在状态更新,界面也更新后(render调用后)才能调用

总结:对象式的setState是函数式的setState的简写方式(语法糖)

       使用原则:

            如果新状态不依赖于原状态 ===>使用对象方式

            如果新状态依赖于原状态 ===>使用函数方式

            如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取

import React, { Component } from 'react'

export default class Demo extends Component {
  state = {
    count: 0
  }
  add = () => {
    // 对象式的函数式的setState
    /*// 1.获取原来的count值
    const {count} = this.state
    // 2.更新状态
    this.setState({
      count:count+1
    }, () => {
      console.log(this.state.count);
    })*/

    // 函数式的setState
    this.setState((state, props) => ({count:state.count+1}))
  }
  render() {
    return (
      <div>
        <h1>当前求和为:{this.state.count}</h1>
        <button onClick={this.add}>点我+1</button>
      </div>
    )
  }
}

二、lazyLoad

const Home = lazy(() => {import('./Home')})

在注册路由的地方将路由包起来,fallback中既可以是一个组件也可以是Dom,不过此组件不可以使用懒加载

<Suspense fallback={<h1>Loading</h1>}></Suspense>

三、Hooks

1.React Hook/Hooks 是什么?

   Hook是React16.8.0版本添加的新特性/新语法

   可以让你在函数组件中使用state以及其他的React特性

2.三个常用的Hook

   (1)State Hook: React.useState()

   (2)Effect Hook: React.useEffect()

   (3)Ref Hook: React.useRef()

4.State Hook

    (1)State Hook让函数式组件可以有state状态,并进行状态数据的读写操作

    (2)语法:const {xxx,setXXX} = React.useState(initValue)

    (3)useState()说明:

        参数:第一次初始化指定的值在内部作缓存

        返回值:包含2个元素的数组,第1个为内部当前状态值,第2个为更新状态的函数

    (4)setXxx()2中写法:

        setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值

        setXxx(value => newValue):参数为函数,接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值

function Demo() {
  const [count, setCount] = React.useState(0)
  const [name, setName] = React.useState('tom')
  function add () {
    // 第一种写法
    setCount(count+1)
    // 第二种写法
    setCount((count) => count +1)
  }
  function changeName() {
    setName('jack')
  }
  return (
    <div>
      <h2>当前求和为{count}</h2>
        <h2>我的名字是:{name}</h2>
        <button onClick={add}>点我+1</button>
        <button onClick={changeName}>点我改名</button>
      
    </div>
  )
}
3.Effect Hook

   (1)Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

   (2)React中的副作用操作:

        发ajax请求数据获取

        设置订阅/启动定时器

        手动更改真是DOM

    (3)语法和说明

        React.useEffect(() => {

          // 在此处可以执行任何副作用操作

          return () => { // 在组件卸载时执行

            // 在此做一些收尾操作,比如清除定时器/取消订阅等

          }

        },[stateValue]) // 如果指定的是[],回调函数只会在第一次render()之后执行

     (4)可以把useEffect Hook看做如下三个函数的组合

        componentDidMount()

        componentDidUpdate()

        componentWillUnmount()

function Demo() {
  const [count, setCount] = React.useState(0)
  React.useEffect(() => {
    let timer = setInterval(() => {
      setCount(count => count +1)
    }, 1000)
    return () => {
      clearInterval()
    }
  },[])
  function add () {
    // 第一种写法
    // setCount(count+1)
    // 第二种写法
    setCount((count) => count +1)
  }
  // 卸载组件的回调
  function unmount() {
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  return (
    <div>
      <h2>当前求和为{count}</h2>
      <button onClick={add}>点我+1</button>      
      <button onClick={unmount}>卸载组件</button>      
    </div>
  )
}
5.useRef

Ref Hook可以在函数组件中存储/查找组件内的标签或任意其他数据

语法:const refContainer = useRef()

作用:保存标签对象,功能与React.createRef()一样

function Demo() {
  const [count, setCount] = React.useState(0)
  const myRef = React.useRef()
  React.useEffect(() => {
    let timer = setInterval(() => {
      setCount(count => count +1)
    }, 1000)
    return () => {
      clearInterval()
    }
  },[])
  function add () {
    // 第一种写法
    // setCount(count+1)
    // 第二种写法
    setCount((count) => count +1)
  }
  // 卸载组件的回调
  function unmount() {
    ReactDOM.unmountComponentAtNode(document.getElementById('root'))
  }
  //提示输入的回调 
  function show() {
    alert(myRef.current.value)
  }
  return (
    <div>
      <input type="text" ref={myRef}/>
      <h2>当前求和为{count}</h2>
      <button onClick={add}>点我+1</button>      
      <button onClick={unmount}>卸载组件</button>      
      <button onClick={show}>点击显示数据</button>      
    </div>
  )
}

四、Fragment

使用<Fragment><Fragment/>    <></>

作用:可以不用必须有一个真实DOM根标签了。

import React, { Component, Fragment } from 'react'

export default class Demo extends Component {
  render() {
    return (
      <Fragment key={1}>
        <input type="text" />
        <input type="text" />
      </Fragment>
    )
  }
}

五、Context

理解:一种组件间通信方式, 常用于[根组件]与【后代组件】间通信

使用:创建Context对象

       const XxxContext = React.createContext()

       渲染子组件的时候,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据

       <XxxContext.Provider value={数据}>子组件</XxxContext.Provider>

后代组件读取数据:

        // 第一种方式:仅适用于类组件

        static contextType = XxxContext // 声明接收context

        this.context // 读取context中的value值

        // 第二种方式: 函数组件与类组件都可以

        <xxxContext.Consumer>

                {

                    value => { // value就是context中的value数据

                    要显示的内容

                    }

                }

        </xxxContext.Consumer>

注意:

      在应用开发中一般不用context,一般都用它的封装react插件

import React, { Component } from 'react'

// 创建Context对象
const MyContext = React.createContext()
export default class Demo extends Component {
  state = {
    username: 'tom'
  }
  render() {
    const {username} = this.state
    return (
      <div>
        <h3>我是A组件</h3>
        <h4>我的用户名是:{username}</h4>
        <MyContext.Provider value={username}>
          <B/>
        </MyContext.Provider>
      </div>
    )
  }
}
class B extends Component {
  render() {
    return (
      <div>
        <h3>我是B组件</h3>
        <C/>
      </div>
    )
  }
}
/*class C extends Component {
  // 声明接收context
  static contextType = MyContext
  render() {
    return (
      <div>
        <h3>我是C组件</h3>
        <h4>我从A组件接收到的用户名是:{this.context}</h4>
      </div>
    )
  }
}
*/
function C(params) {
  return (
    <div>
      <h3>我是C组件</h3>
      <h4>我从A组件接收到的用户名是:
        {/* 类组件和函数组件都可以用 */}
        <MyContext.Consumer>
          {
            value => {
              return value
            }
          }
        </MyContext.Consumer>
      </h4>
    </div>
  )
}

六、组件优化

Component的2个问题

只要执行setState(),即使不改变状态数据,组件也会重新render()

只当前组件重新render(),就会自动重新render子组件==>效率低

效率高的做法:

       只有当组件的state或props数据发生改变时才重新render()

原因:

       Component中的shouldComponentUpdate()总是返回true

解决:

       办法1:重写Component中的shouldComponentUpdate()方法

                    比较新旧state或props数据,如果有变化才返回true,如果没有返回false

        办法2:使用PureComponent

                     PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true

注意:之前的版本

       只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false

       不要直接修改state数据,而是要产生新数据

现在是可以的。

项目中一般使用PureComponent来优化

import React, { Component, PureComponent } from 'react'

export default class Parent extends Component {
  state = {
    carName: 'ccc'
  }
  changeCar = () => {
    this.setState({
      carName: 'vxx'
    })
    // 之前的版本不能修改,但是现在可以的
    // const obj = this.state
    // obj.carName = 'vvv'
    // this.setState(obj)
  }
  // shouldComponentUpdate (nextProps, nextState) {
  //   return !this.state.carName === nextState.carName
  // }
  render() {
    const {carName} = this.state
    return (
      <div>
        <h3>我是Parent组件</h3>
        <h4>我的车名字是:{carName}</h4>
        <button onClick={this.changeCar}>huanche</button>
        <Child carName={carName}/>
      </div>
    )
  }
}

class Child extends PureComponent {
  // shouldComponentUpdate (nextProps, nextState) {
  //   return !this.props.carName === nextProps.carName
  // }
  render() {
    return (
      <div>
        <h3>我是Child组件</h3>
        <span>我接到的是{this.props.carName}</span>
      </div>
    )
  }
}

七、render Props

如何向组件内部动态传入带内容的结构(标签)?

Vue中:

     使用slot技术,也就是通过组件标签体传入结构

React中:

     使用children props:通过组件标签体传入结构

     使用render props:通过组件标签属性传入结构,一般用render函数属性

Children props

     <A>

          <B>xxxx<B/>

     </A>

     {this.props.children}

问题:如果B组件需要A组件内的数据, ==>做不到

render props

    A组件:<A render={(data) => <C data={data}></C>}></A>

    C组件:读取A组件传入的数据显示:{this.props.data}

import React, { Component } from 'react'

export default class Parent extends Component {
  render() {
    return (
      <div>
        我是Parent组件
        {/*
        没有参数的情况
          <A>
            <B/>
          <A/> 
        */}
        {/* 有参数的情况 */}
        <A render={(name) => <B name={name}/>}/>
      </div>
    )
  }
}

class A extends Component {
  state = {
    name: 'tom'
  }
  render() {
    const {name} = this.state
    return (
      <div>
        我是A组件
        {/* 没有参数的情况 */}
        {/* {this.props.children} */}
        {/* 有参数的情况 */}
        {this.props.render(name)}
      </div>
    )
  }
}

class B extends Component {
  render() {
    return (
      <div>
        我是B组件
        {this.props.name}
      </div>
    )
  }
}

八、错误边界

理解:错误边界(Error boundary),用来捕获后代组件错误,渲染出备用页面

特点:只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件,定时器中产生的错误

使用方式:

       getDerivedStateFromError配合componentDidCatch

生命周期函数,一旦后台组件报错,就会触发

static getDeriverdStateFromError(error) {

   console.log(error)

   // 在render之前触发

   // 返回新的state

   return  {

      hasError: true

   }

}

componentDidCatch(error,info) {

    // 统计页面的错误,发送请求发送到后台去

    console.log(error,info)

}

具体代码示例:

// Parent.jsx
import React, { Component } from 'react'
import Child from './Child'
export default class Parent extends Component {
  state = {
    hasError: '' // 用于标识子组件是否产生错误
  }
  static getDerivedStateFromError (error) {
    // 当Parent的子组件出现报错的时候,会触发getDerivedStateFromError调用,并携带错误信息
    console.log(error)
    return {
      hasError: true
    }
  }
  componentDidCatch() {
    console.log('统计错误次数,反馈给服务器,用于通知编码人员进行bug解决');
  }
  render() {
    return (
      <div>
        <h2>我是Parent组件</h2>
        {this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2>: <Child/>}
      </div>
    )
  }
}



// Child.jsx
import React, { Component } from 'react'

export default class Child extends Component {
  state = {
    // users: [
    //   {id: '001', name: 'tom', age: 18},
    //   {id: '002', name: 'jacom', age: 19},
    //   {id: '003', name: 'piy', age: 20},
    // ]
    users: 'dddd'
  }
  render() {
    return (
      <div>
        <h2>我是Child组件</h2>
        {
          this.state.users.map((userObj) => {
            return (<h4 key={userObj.id}>
              {userObj.name}---{userObj.age}
            </h4>)
          })
        }
      </div>
    )
  }
}

九、组件间通信的方式

1.组件间的关系

父子组件

兄弟组件(非嵌套组件)

祖孙组件(跨级组件)

2.几种通信方式

props:

   (1)children props

   (2)render props

消息订阅-发布

   pubs-sub,event等

集中式管理:

   redux、dva等等

conText:

   生产者-消费者模式

比较好的搭配方式

   父子组件:props

   兄弟组件:消息订阅-发布,集中式管理

   祖孙组件(跨级组件):消息订阅-发布,集中式管理,conText(开发用的少,封装插件的多)

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值