react拓展

setState()

setState更新状态的2种方式

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

1. stateChange为状态改变对象
2. callback是可选的回调函数,它在状态更新完毕,界面也更新后(render调用后)才被调用
  1. 函数式的setState
setState(updater, [callback])
1. updater为返回stateChange对象的函数
2. updater可以接收到state和props
3. callback是可选的回调函数,它在状态更新、界面更新后(render调用后)才被调用

state = { count: 0 }

add = () => {
  // 对象式的setState
  const { count } = this.state // 0
  this.setState({ count: count + 1}, () => {
    console.log(this.state.count) // 1
  })
    console.log(this.state.count) // 0
  
  // 函数式编程
  /*
  this.setState((state,props) => {
    return {
      count: state.count + 1
    } 
  })
  **/
  this.setState(state => ({count: state.count + 1}))
  
}

对象式的setState是函数式的setState的简写方式

使用原则:

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

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

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

lazyLoad

路由组件的lazyLoad

import React, { Component, lazy, Suspense } from 'react'

const Loading from './Loading'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))


<Suspense fallback={<Loading />}>
  {/* 注册组件 */}
    <Route path="/about" component={About}>
  <Route path="/home" component={Home}>
</Suspense>


Hooks

React Hook/Hooks是什么?

  1. Hook是React 16.8.0版本增加的新特性/语法
  2. 可以使在函数组件中使用state以及其他的React特性

三个常用的Hook

  1. State Hook:React.useState()

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

    2. 语法:const [xxx, setXxx] = React.useState(initValue)

    3. useState()说明

      1. 参数:第一次初始化制定的值在内部做换粗
      2. 返回值:包含2个元素的数组,第一个为内部值当前状态值,第二个为更新状态值的函数
    4. setXxx()的2种写法:

      1. setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的值
      2. setXxx(value => newValue):参数作为函数,接受原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
    5. function Test () {
          console.log('Test组件')
          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>
                  <h1>Hooks</h1>
                  <h2>当前数值为{ count }</h2>
                  <p>我的名字是:{ name }</p>
                  <button onClick={add}>增加</button>
                  <button onClick={changeName}>更改名称</button>
              </div>
          )
      }
      export default Test
      
  2. Effect Hook:React.useEffect()

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

    2. React中的副作用操作

      1. 发送ajax操作
      2. 设置订阅/启动定时器
      3. 手动更改真实DOM
    3. 语法说明:

      1. useEffect(() => {
           // 在此可以执行任何带副作用的操作
          return () => {
            // 在此做一些收尾工作,比如:清除定时器/取消订阅等
          }
        },[stateValue]) // 如果制定的是[],回调函数只会在第一次render()后执行
        
      2. 可以把 useEffect Hook 看做如下三个函数的组合

        1. componentDidMount()
          componentDidUpdate()
          componentWillUnmount()
          
      3. function Test () {
            console.log('Test组件')
            const [count, setCount] = React.useState(0)
            React.useEffect(() => {
                let timer = setInterval(() => {
                    setCount(count => count + 1)
                }, 1000);
                return () => {
                    console.log('卸载组件####')
                    clearInterval(timer)
                }
            },[])
            function unMount() {
                reactDom.unmountComponentAtNode(document.getElementById('root'))
            }
            return(
                <div>
                    <h1>Hooks</h1>
                    <h2>当前求和为{ count }</h2>
                    <button onClick={unMount}>卸载组件</button>
                </div>
            )
        }
        
  3. Ref Hook

    1. Ref Hook 可以在函数组件中存储/查找组件内的标签或任意其他数据
    2. 语法:const refContainer = React.useRef()
    3. 作用:保存标签对象,功能与React.createRef() 一样
function Test () {
    const refContainer = React.useRef()
    function show() {
        alert(refContainer.current.value)
    }
    return(
        <div>
            <input type="text" ref={ refContainer } />
            <button onClick={ show }>点击提示当前输入内容</button>
        </div>
    )
}

Fragment

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

<Fragment>
        <input type="test" />
    <input type="test" />
</Fragment>

// 编译后
<input type="test" />
<input type="test" />

Context

一种组件间通信方式,常用于[祖组件]与[后代组件]间通信,一般都用于封装react插件

使用:

import React, { Component } from 'react'

// 创建一个Context对象
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext
export default class ContextComponent extends Component {
    state = { userName: '张三', age: 18 }
    render() {
        const { userName, age } = this.state
        return (
            <div>
                <h2>Context</h2>
                <Provider value={{userName, age}}>
                    <A />
                </Provider>   
            </div>
        )
    }
}

class A extends Component {
    render() {
        return(
            <div>
                <h3>我是a组件</h3>
                <B />
            </div>
        )
    }
}

class B extends Component {
    state = { userName: 'tom' }
    render() {
        return(
            <div>
                <h4>我是B组件</h4>
                <C1 />
                <C2 />
            </div>
        )
    }
}

// 类式组件
class C1 extends Component {
    // 声明context
    static contextType = MyContext
    state = { userName: 'tom' }
    render() {
        return(
            <div>
                <h5>我是C1组件</h5>
                <h5>Context接收到的用户名:{ this.context.userName }, 年龄:{ this.context.age }</h5>
            </div>
        )
    }
}

// 函数组件、类式组件都可以
function C2 () {
    return(
        <div>
            <h5>我是C2组件</h5>
            <h5>Context接收到的
                
                <Consumer>
                    {/* value 就是 context 中的 value 数据 */}
                    { value => `用户名:${ value.userName },年龄:${ value.age }` }
                </Consumer>
            </h5>
        </div>
    )
}

组件优化

component的2个问题

  1. 只要执行setState(),即使不改变状态数据,组件也会重新render()
  2. 当前组件重新render(),会自动重新render()子组件,即使没使用父组件的任何数据 ===> 效率低

效率高的做法

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

原因

component中的shouldComponentUpdate()总是返回true

  1. 解决一:重写shouldComponentUpdate()方法,比较新旧state或props数据,如果有变化才返回true,如果没有返回false
export default class Car extends Component {
    state = { carName: '奔驰63' }
    changeCar = () => {
        this.setState({
            carName: '迈巴赫'
        })
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log(this.props, this.state) // 当前props和state
        console.log(nextProps, nextState) // 接下来要变化的props和state
        return !this.state.carName === nextState.carName
    }
    render() {
        console.log('Car-parent render')
        const { carName } = this.state
        return (
            <div>
                <h3>我是Parent组件</h3>
                <p>我的车名字时:{carName}</p>
                <button onClick={this.changeCar}>点击换车</button>
                <Child carName={carName} />
            </div>
        )
    }
}

class Child extends Component {
    render() {
        console.log('Car-child render')
        const { carName } = this.props
        return (
            <div>
                <h4>我是Child组件</h4>
                <p>我接到的车是:{ carName }</p>
            </div>
        )
    }
}
  1. 解决二:使用PureComponent,PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true
import React, { PureComponent } from 'react'

export default class Car extends PureComponent {
    state = { carName: '奔驰63' }
    changeCar = () => {
        this.setState({
            carName: '迈巴赫'
        })
    }
    render() {
        console.log('Car-parent render')
        const { carName } = this.state
        return (
            <div>
                <h3>我是Parent组件</h3>
                <p>我的车名字时:{carName}</p>
                <button onClick={this.changeCar}>点击换车</button>
                <Child carName={carName} />
            </div>
        )
    }
}

class Child extends PureComponent {
    render() {
        console.log('Car-child render')
        const { carName } = this.props
        return (
            <div>
                <h4>我是Child组件</h4>
                <p>我接到的车是:{ carName }</p>
            </div>
        )
    }
}

注意⚠️: 只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false。不能直接修改state数据,而是要产生新数据。项目中一般使用PureComponent来优化

render props

  1. children props:通过组件标签体传入结构

    1. <A>
        <B></B>
      </A>
      
      { this.props.children } // 如果B组件需要A组件内的数据  ===> 做不到
      
  2. Render props:通过组件标签属性传入结构,一般使用render函数属性

    1. <A render={ (data) => <C data ={ data }></C>}></A>
      // A 组件
      { this.props.render(内部state数据) }
      // C 组件 --- 读取A组件传入的数据显示
      { this.props.data }
      
      

Error boundary

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

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

使用方式

static getDerivedStateFromError(error){
  console.log(error)
  return {
    hasError: true
  }
}

componentDidCatch(error, info) {
  // 统计页面的错误,发送请求到服务端
  console.log(error, info)
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容