react拓展

setState()

setState更新狀態(tài)的2種方式

  1. 對(duì)象式的setState
setState(stateChange,[callback])

1. stateChange為狀態(tài)改變對(duì)象
2. callback是可選的回調(diào)函數(shù),它在狀態(tài)更新完畢售碳,界面也更新后(render調(diào)用后)才被調(diào)用
  1. 函數(shù)式的setState
setState(updater, [callback])
1. updater為返回stateChange對(duì)象的函數(shù)
2. updater可以接收到state和props
3. callback是可選的回調(diào)函數(shù)搪锣,它在狀態(tài)更新耳鸯、界面更新后(render調(diào)用后)才被調(diào)用

state = { count: 0 }

add = () => {
  // 對(duì)象式的setState
  const { count } = this.state // 0
  this.setState({ count: count + 1}, () => {
    console.log(this.state.count) // 1
  })
    console.log(this.state.count) // 0
  
  // 函數(shù)式編程
  /*
  this.setState((state,props) => {
    return {
      count: state.count + 1
    } 
  })
  **/
  this.setState(state => ({count: state.count + 1}))
  
}

對(duì)象式的setState是函數(shù)式的setState的簡(jiǎn)寫方式

使用原則:

1.如果新狀態(tài)不依賴于原狀態(tài) ====> 使用對(duì)象方式

2.如果新狀態(tài)依賴于原狀態(tài) =====> 使用函數(shù)方式

3.如果需要在setState()執(zhí)行后獲取最新的狀態(tài)數(shù)據(jù)秸仙,需要在第二個(gè)callback函數(shù)中讀取

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 />}>
  {/* 注冊(cè)組件 */}
    <Route path="/about" component={About}>
  <Route path="/home" component={Home}>
</Suspense>


Hooks

React Hook/Hooks是什么聂薪?

  1. Hook是React 16.8.0版本增加的新特性/語法
  2. 可以使在函數(shù)組件中使用state以及其他的React特性

三個(gè)常用的Hook

  1. State Hook:React.useState()

    1. State Hook 讓函數(shù)組件也可以有state狀態(tài)部逮,并進(jìn)行狀態(tài)數(shù)據(jù)的讀寫操作

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

    3. useState()說明

      1. 參數(shù):第一次初始化制定的值在內(nèi)部做換粗
      2. 返回值:包含2個(gè)元素的數(shù)組枷恕,第一個(gè)為內(nèi)部值當(dāng)前狀態(tài)值,第二個(gè)為更新狀態(tài)值的函數(shù)
    4. setXxx()的2種寫法:

      1. setXxx(newValue):參數(shù)為非函數(shù)值寻咒,直接指定新的狀態(tài)值哮翘,內(nèi)部用其覆蓋原來的值
      2. setXxx(value => newValue):參數(shù)作為函數(shù),接受原本的狀態(tài)值毛秘,返回新的狀態(tài)值饭寺,內(nèi)部用其覆蓋原來的狀態(tài)值
    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>當(dāng)前數(shù)值為{ 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 可以讓你在函數(shù)組件中執(zhí)行副作用操作(用于模擬類組件中的生命周期鉤子)

    2. React中的副作用操作

      1. 發(fā)送ajax操作
      2. 設(shè)置訂閱/啟動(dòng)定時(shí)器
      3. 手動(dòng)更改真實(shí)DOM
    3. 語法說明:

      1. useEffect(() => {
           // 在此可以執(zhí)行任何帶副作用的操作
          return () => {
            // 在此做一些收尾工作,比如:清除定時(shí)器/取消訂閱等
          }
        },[stateValue]) // 如果制定的是[]叫挟,回調(diào)函數(shù)只會(huì)在第一次render()后執(zhí)行
        
      2. 可以把 useEffect Hook 看做如下三個(gè)函數(shù)的組合

        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>當(dāng)前求和為{ count }</h2>
                    <button onClick={unMount}>卸載組件</button>
                </div>
            )
        }
        
  3. Ref Hook

    1. Ref Hook 可以在函數(shù)組件中存儲(chǔ)/查找組件內(nèi)的標(biāo)簽或任意其他數(shù)據(jù)
    2. 語法:const refContainer = React.useRef()
    3. 作用:保存標(biāo)簽對(duì)象艰匙,功能與React.createRef() 一樣
function Test () {
    const refContainer = React.useRef()
    function show() {
        alert(refContainer.current.value)
    }
    return(
        <div>
            <input type="text" ref={ refContainer } />
            <button onClick={ show }>點(diǎn)擊提示當(dāng)前輸入內(nèi)容</button>
        </div>
    )
}

Fragment

可以不用必須有一個(gè)真實(shí)DOM根標(biāo)簽了

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

// 編譯后
<input type="test" />
<input type="test" />

Context

一種組件間通信方式,常用于[祖組件]與[后代組件]間通信霞揉,一般都用于封裝react插件

使用:

import React, { Component } from 'react'

// 創(chuàng)建一個(gè)Context對(duì)象
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>
        )
    }
}

// 函數(shù)組件旬薯、類式組件都可以
function C2 () {
    return(
        <div>
            <h5>我是C2組件</h5>
            <h5>Context接收到的
                
                <Consumer>
                    {/* value 就是 context 中的 value 數(shù)據(jù) */}
                    { value => `用戶名:${ value.userName },年齡:${ value.age }` }
                </Consumer>
            </h5>
        </div>
    )
}

組件優(yōu)化

component的2個(gè)問題

  1. 只要執(zhí)行setState(),即使不改變狀態(tài)數(shù)據(jù)适秩,組件也會(huì)重新render()
  2. 當(dāng)前組件重新render()绊序,會(huì)自動(dòng)重新render()子組件,即使沒使用父組件的任何數(shù)據(jù) ===> 效率低

效率高的做法

只有當(dāng)組件的state或props數(shù)據(jù)發(fā)生改變時(shí)才重新render()

原因

component中的shouldComponentUpdate()總是返回true

  1. 解決一:重寫shouldComponentUpdate()方法秽荞,比較新舊state或props數(shù)據(jù)骤公,如果有變化才返回true,如果沒有返回false
export default class Car extends Component {
    state = { carName: '奔馳63' }
    changeCar = () => {
        this.setState({
            carName: '邁巴赫'
        })
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log(this.props, this.state) // 當(dāng)前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>我的車名字時(shí):{carName}</p>
                <button onClick={this.changeCar}>點(diǎn)擊換車</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數(shù)據(jù)有變化才返回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>我的車名字時(shí):{carName}</p>
                <button onClick={this.changeCar}>點(diǎn)擊換車</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>
        )
    }
}

注意??: 只是進(jìn)行state和props數(shù)據(jù)的淺比較阶捆,如果只是數(shù)據(jù)對(duì)象內(nèi)部數(shù)據(jù)變了,返回false。不能直接修改state數(shù)據(jù)洒试,而是要產(chǎn)生新數(shù)據(jù)倍奢。項(xiàng)目中一般使用PureComponent來優(yōu)化

render props

  1. children props:通過組件標(biāo)簽體傳入結(jié)構(gòu)

    1. <A>
        <B></B>
      </A>
      
      { this.props.children } // 如果B組件需要A組件內(nèi)的數(shù)據(jù)  ===> 做不到
      
  2. Render props:通過組件標(biāo)簽屬性傳入結(jié)構(gòu),一般使用render函數(shù)屬性

    1. <A render={ (data) => <C data ={ data }></C>}></A>
      // A 組件
      { this.props.render(內(nèi)部state數(shù)據(jù)) }
      // C 組件 --- 讀取A組件傳入的數(shù)據(jù)顯示
      { this.props.data }
      
      

Error boundary

錯(cuò)誤邊界(Error boundary):用來捕獲后代組件錯(cuò)誤垒棋,渲染出備用頁面

只能捕獲后代組件生命周期產(chǎn)生的錯(cuò)誤卒煞,不能捕獲自己組件產(chǎn)生的錯(cuò)誤和其他組件在合成事件、定時(shí)器中產(chǎn)生的錯(cuò)誤

使用方式

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

componentDidCatch(error, info) {
  // 統(tǒng)計(jì)頁面的錯(cuò)誤叼架,發(fā)送請(qǐng)求到服務(wù)端
  console.log(error, info)
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末畔裕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子乖订,更是在濱河造成了極大的恐慌扮饶,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乍构,死亡現(xiàn)場(chǎng)離奇詭異甜无,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蜡吧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門毫蚓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人昔善,你說我怎么就攤上這事∨弦遥” “怎么了君仆?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)牲距。 經(jīng)常有香客問我返咱,道長(zhǎng),這世上最難降的妖魔是什么牍鞠? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任咖摹,我火速辦了婚禮,結(jié)果婚禮上难述,老公的妹妹穿的比我還像新娘萤晴。我一直安慰自己,他們只是感情好胁后,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布店读。 她就那樣靜靜地躺著,像睡著了一般攀芯。 火紅的嫁衣襯著肌膚如雪屯断。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音殖演,去河邊找鬼氧秘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛趴久,可吹牛的內(nèi)容都是我干的敏储。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼朋鞍,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼已添!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滥酥,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤更舞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后坎吻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缆蝉,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年瘦真,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了刊头。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诸尽,死狀恐怖原杂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情您机,我是刑警寧澤穿肄,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站际看,受9級(jí)特大地震影響咸产,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仲闽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一脑溢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赖欣,春花似錦屑彻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至云矫,卻和暖如春膳沽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工挑社, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留陨界,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓痛阻,卻偏偏與公主長(zhǎng)得像菌瘪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阱当,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容