React 核心API

起步

  1. 安裝官方腳手架: npm install -g create-react-app

  2. 創(chuàng)建項目: create-react-app react-study

  3. 啟動項目: npm start

文件結(jié)構(gòu)

文件結(jié)構(gòu)一覽

├── README.md 文檔
├── public 靜態(tài)資源
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src 源碼
├── App.css
├── App.js 根組件
├── App.test.js
├── index.css 全局樣式
├── index.js 入口文件
├── logo.svg
└── serviceWorker.js pwa 支持
├── package.json npm 依賴

cra 項目真容

使用 npm run eject 彈出項目真面目,會多出兩個目錄:

├── config
├── env.js 處理.env 環(huán)境變量配置文件
├── paths.js 提供各種路徑
├── webpack.config.js webpack 配置文件
└── webpackDevServer.config.js 測試服務(wù)器配置文件
└── scripts 啟動、打包和測試腳本
├── build.js 打包腳本、
├── start.js 啟動腳本
└── test.js 測試腳本

env.js 用來處理.env 文件中配置的環(huán)境變量

// node運(yùn)行環(huán)境:development、production、test等
const NODE_ENV = process.env.NODE_ENV
// 要掃描的文件名數(shù)組
var dotenvFiles = [
  `${paths.dotenv}.${NODE_ENV}.local`, // .env.development.local
  `${paths.dotenv}.${NODE_ENV}`, // .env.development
  NODE_ENV !== 'test' && `${paths.dotenv}.local`, // .env.local
  paths.dotenv // .env
].filter(Boolean)
// 從.env*文件加載環(huán)境變量
dotenvFiles.forEach(dotenvFile => {
  if (fs.existsSync(dotenvFile)) {
    require('dotenv-expand')(
      require('dotenv').config({
        path: dotenvFile
      })
    )
  }
})

實踐一下,修改一下默認(rèn)端口號,創(chuàng)建.env 文件

PORT = 8080

webpack.config.js 是 webpack 配置文件,開頭的常量聲明可以看出 cra 能夠支持 ts、sass 及 css 模塊化

// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig)
// style files regexes
const cssRegex = /\.css$/
const cssModuleRegex = /\.module\.css$/
const sassRegex = /\.(scss|sass)$/
const sassModuleRegex = /\.module\.(scss|sass)$/

React 和 ReactDOM

刪除 src 下面所有代碼,新建 index.js

import React from 'react'
import ReactDOM from 'react-dom'

// React類負(fù)責(zé)邏輯控制,比如修改數(shù)據(jù) -> vdom
// ReactDOM類負(fù)責(zé)渲染,vdom -> dom
// babel-loader可以轉(zhuǎn)換 jsx -> vdom,React.createElement()
// <h1>React</h1> => React.createElement('h1','React')
const jsx = <h1>React</h1>
console.log(jsx)

ReactDOM.render(jsx, document.getElementById('root'))

React 負(fù)責(zé)邏輯控制,數(shù)據(jù) -> VDOM
ReactDom 渲染實際 DOM,VDOM -> DOM,如果換到移動端,就用別的庫來渲染
React 使用 JSX 來描述 UI
入口文件定義,webpack.config.js

entry: [
  // WebpackDevServer客戶端,它實現(xiàn)開發(fā)時熱更新功能
  isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
  // 應(yīng)用程序入口:src/index
  paths.appIndexJs
].filter(Boolean)

JSX

JSX 是一種 JavaScript 的語法擴(kuò)展,其格式比較像模版語言,但事實上完全是在 JavaScript 內(nèi)部實現(xiàn)的。

JSX 可以很好地描述 UI,能夠有效提高開發(fā)效率

使用 JSX

表達(dá)式{}的使用,index.js

// 只要是合法的表達(dá)式
const name = 'react study'
const jsx = <h2>{name}</h2>

函數(shù)也是合法表達(dá)式,index.js

const user = { firstName: 'micheal', lastName: 'jordan' }
function formatName(user) {
  return user.firstName + ' ' + user.lastName
}
const jsx = <h2>{formatName(user)}</h2>

jsx 是 js 對象,也是合法表達(dá)式,index.js

const greet = <p>hello, Jerry</p>
const jsx = <h2>{greet}</h2>

條件語句可以基于上面結(jié)論實現(xiàn),index.js

const showTitle = true
const title = name ? <h2>{name}</h2> : null
const jsx = (
  <div>
    {/* 條件語句 */}
    {title}
  </div>
)

數(shù)組會被作為一組子元素對待,數(shù)組中存放一組 jsx 可用于顯示列表數(shù)據(jù)

const arr = [1, 2, 3].map(num => <li key={num}>{num}</li>)
const jsx = (
  <div>
    {/* 數(shù)組 */}
    <ul>{arr}</ul>
  </div>
)

屬性的使用

import logo from './logo.svg'
const jsx = (
  <div>
    {/* 屬性:靜態(tài)值用雙引號,動態(tài)值用花括號;class、for等要特殊處理。 */}
    <img src={logo} style={{ width: 100 }} className="img" />
  </div>
)

css 模塊化,創(chuàng)建 index.module.css,index.js

import style from './index.module.css'
const img = <img className={style.img} />

組件

組件時抽象的獨立功能模塊,react 應(yīng)用程序由組件構(gòu)建而成。

組件的兩種形式

組件有兩種形式:function 組件class 組件

class 組件

class 組件通常擁有狀態(tài)生命周期繼承于 Component實現(xiàn) render 方法

提取前面 jsx 相關(guān)代碼至 components/JsxTest.js

import React, { Component } from 'react'
import logo from '../logo.svg'
import style from '../index.module.css'
export default class JsxTest extends Component {
  render() {
    const name = 'react study'
    const user = { firstName: 'tom', lastName: 'jerry' }
    function formatName(user) {
      return user.firstName + ' ' + user.lastName
    }
    const greet = <p>hello, Jerry</p>
    const arr = [1, 2, 3].map(num => <li key={num}>{num}</li>)
    return (
      <div>
        {/* 條件語句 */}
        {name ? <h2>{name}</h2> : null}
        {/* 函數(shù)也是表達(dá)式 */}
        {formatName(user)}
        {/* jsx也是表達(dá)式 */}
        {greet}
        {/* 數(shù)組 */}
        <ul>{arr}</ul>
        {/* 屬性 */}
        <img src={logo} className={style.img} alt="" />
      </div>
    )
  }
}

創(chuàng)建并指定 src/App.js 為根組件

import React from 'react'
import JsxTest from './components/JsxTest'
function App() {
  return (
    <div>
      <JsxTest />
    </div>
  )
}
export default App

index.js 中使用 App 組件

import App from './App'

ReactDOM.render(<App title="hello" />, document.getElementById('root'))
function 組件

函數(shù)組件通常無狀態(tài),僅關(guān)注內(nèi)容展示,返回渲染結(jié)果即可。

改造 App.js

import React from 'react'
import JsxTest from './components/JsxTest'
function App() {
  return (
    <div>
      <JsxTest />
    </div>
  )
}
export default App

從 React16.8 開始引入了 hooks,函數(shù)組件也能夠擁有狀態(tài),后面組件狀態(tài)管理部分討論

組件狀態(tài)管理

如果組件中數(shù)據(jù)會變化,并影響頁面內(nèi)容,則組件需要擁有狀態(tài) (state) 并維護(hù)狀態(tài)。

類組件中的狀態(tài)管理

class 組件使用 state 和 setState 維護(hù)狀態(tài)

范例:創(chuàng)建狀態(tài)管理組件 componentes/StateMgt.js

export default function StateMgt() {
  return (
    <div>
      <Clock />
    </div>
  )
}

創(chuàng)建一個 Clock 組件

class Clock extends React.Component {
  constructor(props) {
    super(props)
    // 使用state屬性維護(hù)狀態(tài),在構(gòu)造函數(shù)中初始化狀態(tài)
    this.state = { date: new Date() }
  }

  componentDidMount() {
    // 組件掛載時啟動定時器每秒更新狀態(tài)
    this.timerID = setInterval(() => {
      // 使用setState方法更新狀態(tài)
      this.setState({
        date: new Date()
      })
    }, 1000)
  }

  componentWillUnmount() {
    // 組件卸載時停止定時器
    clearInterval(this.timerID)
  }

  render() {
    return <div>{this.state.date.toLocaleTimeString()}</div>
  }
}

setState 特性討論

  • 用 setState 更新狀態(tài)而不能直接修改
this.state.counter += 1 //錯誤的
  • setState 是批量進(jìn)行的,因此對同一個狀態(tài)執(zhí)行多次只起一次作用,多個狀態(tài)更新可以放在同一個 setSate 中進(jìn)行:
componentDidMount() {
// 假如couter初始值為0,執(zhí)行三次以后其結(jié)果是多少?
this.setState({counter: this.state.counter + 1});
this.setState({counter: this.state.counter + 1});
this.setState({counter: this.state.counter + 1});
}
  • setState 通常是異步的,因此如果要獲取到最新狀態(tài)值有以下三種方式:

    1.傳遞函數(shù)給 setState 方法

    this.setState(state => {
      console.log(state.counter)
      return { counter: state.counter + 1 }
    }) // 0
    this.setState(state => {
      console.log(state.counter)
      return { counter: state.counter + 1 }
    }) // 1
    this.setState(state => {
      console.log(state.counter)
      return { counter: state.counter + 1 }
    }) // 2
    

    2.使用定時器:

    setTimeout(() => {
      console.log(this.state.counter)
    }, 0)
    

    3.原生事件中修改狀態(tài)

    componentDidMount(){
        document.body.addEventListener('click', this.changeValue, false)
    }
    changeValue = () => {
        this.setState({counter: this.state.counter+1})
        console.log(this.state.counter)
    }
    
函數(shù)組件中的狀態(tài)管理

函數(shù)組件通過 hooks api 維護(hù)狀態(tài)

import { useState, useEffect } from 'react'
function ClockFunc() {
  // useState創(chuàng)建一個狀態(tài)和修改該狀態(tài)的函數(shù)
  const [date, setDate] = useState(new Date())
  // useEffect編寫副作用代碼
  useEffect(() => {
    // 啟動定時器是我們的副作用代碼
    const timerID = setInterval(() => {
      setDate(new Date())
    }, 1000)
    // 返回清理函數(shù)
    return () => clearInterval(timerID)
  }, []) // 參數(shù)2傳遞空數(shù)組使我們參數(shù)1函數(shù)僅執(zhí)行一次
  return <div>{date.toLocaleTimeString()}</div>
}

hooks api 后面會繼續(xù)深入學(xué)習(xí)

事件處理

React 中使用 onXx 寫法來監(jiān)聽事件。

范例:用戶輸入事件,創(chuàng)建 EventHandle.js

import React, { Component } from 'react'
export default class EventHandle extends Component {
  constructor(props) {
    super(props)
    this.state = {
      name: ''
    }
    // this.handleChange = this.handleChange.bind(this)
  }

  //   handleChange(e) {
  //     this.setState({ name: e.target.value })
  //   }
  handleChange = e => {
    this.setState({ name: e.target.value })
  }

  render() {
    return (
      <div>
        {/* 使用箭頭函數(shù),不需要指定回調(diào)函數(shù)this,且便于傳遞參數(shù) */}
        {/* <input
        type="text"
        value={this.state.name}
        onChange={e => this.handleChange(e)}
        /> */}
        {/* 直接指定回調(diào)函數(shù),需要指定其this指向,或者將回調(diào)設(shè)置為箭頭函數(shù)屬性 */}
        <input
          type="text"
          value={this.state.name}
          onChange={this.handleChange}
        />
        <p>{this.state.name}</p>
      </div>
    )
  }
}

組件通信

Props 屬性傳遞

Props 屬性傳遞可用于父子組件相互通信

// index.js
ReactDOM.render(<App title="開課吧真不錯" />, document.querySelector('#root'))
// App.js
const jsx = <h2>{this.props.title}</h2>

如果父組件傳遞的是函數(shù),則可以把子組件信息傳入父組件,這個常稱為狀態(tài)提升,StateMgt.js

// StateMgt
const jsx = <Clock change={date => console.log(date)} />

// Clock
this.timerID = setInterval(() => {
  this.setState(
    {
      date: new Date()
    },
    () => {
      // 每次狀態(tài)更新就通知父組件
      this.props.change(this.state.date)
    }
  )
}, 1000)

context

跨層級組件之間通信

主要用于組件庫開發(fā)中,后面組件化內(nèi)容中詳細(xì)學(xué)習(xí)

redux

類似 vuex,無明顯關(guān)系的組件間通信

后面全家桶部分詳細(xì)學(xué)習(xí)

生命周期

范例:驗證生命周期,創(chuàng)建 Lifecycle.js

import React, { Component } from 'react'
export default class Lifecycle extends Component {
  constructor(props) {
    super(props)
    // 常用于初始化狀態(tài)
    console.log('1.組件構(gòu)造函數(shù)執(zhí)行')
  }
  componentWillMount() {
    // 此時可以訪問狀態(tài)和屬性,可進(jìn)行api調(diào)用等
    console.log('2.組件將要掛載')
  }
  componentDidMount() {
    // 組件已掛載,可進(jìn)行狀態(tài)更新操作
    console.log('3.組件已掛載')
  }
  componentWillReceiveProps(nextProps, nextState) {
    // 父組件傳遞的屬性有變化,做相應(yīng)響應(yīng)
    console.log('4.將要接收屬性傳遞')
  }
  shouldComponentUpdate(nextProps, nextState) {
    // 組件是否需要更新,需要返回布爾值結(jié)果,優(yōu)化點
    console.log('5.組件是否需要更新?')
    return true
  }
  componentWillUpdate() {
    // 組件將要更新,可做更新統(tǒng)計
    console.log('6.組件將要更新')
  }
  componentDidUpdate() {
    // 組件更新
    console.log('7.組件已更新')
  }
  componentWillUnmount() {
    // 組件將要卸載, 可做清理工作
    console.log('8.組件將要卸載')
  }
  render() {
    console.log('組件渲染')
    return <div>生命周期探究</div>
  }
}

激活更新階段:App.js

class App extends Component {
  state = { prop: 'some content' }
  componentDidMount() {
    this.setState({ prop: 'new content' })
  }
  render() {
    return (
      <div>
        <Lifecycle prop={this.state.prop} />
      </div>
    )
  }
}

激活卸載階段,App.js

class App extends Component {
  state = { prop: 'some content' }
  componentDidMount() {
    this.setState({ prop: 'new content' })
    setTimeout(() => {
      this.setState({ prop: '' })
    }, 2000)
  }
  render() {
    return <div>{this.state.prop && <Lifecycle prop={this.state.prop} />}</div>
  }
}

生命周期探究

React v16.x 前的生命周期

第一個是組件初始化(initialization)階段

也就是以下代碼中類的構(gòu)造方法( constructor() ),Test 類繼承了 react Component 這個基類,也就繼承這個 react 的基類,才能有 render(),生命周期等方法可以使用,這也說明為什么 函數(shù)組件不能使用這些方法 的原因。

super(props) 用來調(diào)用基類的構(gòu)造方法( constructor() ), 也將父組件的 props 注入給子組件,功子組件讀取(組件中 props 只讀不可變,state 可變)。 而 constructor() 用來做一些組件的初始化工作,如定義 this.state 的初始內(nèi)容。

import React, { Component } from 'react'
class Test extends Component {
  constructor(props) {
    super(props)
  }
}
第二個是組件的掛載(Mounting)階段

此階段分為 componentWillMount,render,componentDidMount 三個時期。

  • componentWillMount:
    在組件掛載到 DOM 前調(diào)用,且只會被調(diào)用一次,在這邊調(diào)用 this.setState 不會引起組件重新渲染,也可以把寫在這邊的內(nèi)容提前到 constructor()中,所以項目中很少用。

  • render:
    根據(jù)組件的 props 和 state(無兩者的重傳遞和重賦值,論值是否有變化,都可以引起組件重新 render) ,return 一個 React 元素(描述組件,即 UI),不負(fù)責(zé)組件實際渲染工作,之后由 React 自身根據(jù)此元素去渲染出頁面 DOM。render 是純函數(shù)(Pure function:函數(shù)的返回結(jié)果只依賴于它的參數(shù);函數(shù)執(zhí)行過程里面沒有副作用),不能在里面執(zhí)行 this.setState,會有改變組件狀態(tài)的副作用。

  • componentDidMount:
    組件掛載到 DOM 后調(diào)用,且只會被調(diào)用一次

第三個是組件的更新(update)階段

在講述此階段前需要先明確下 react 組件更新機(jī)制。setState 引起的 state 更新或父組件重新 render 引起的 props 更新,更新后的 state 和 props 相對之前無論是否有變化,都將引起子組件的重新 render。詳細(xì)可看這篇文章

造成組件更新有兩類(三種)情況:
  • 父組件重新 render
    父組件重新 render 引起子組件重新 render 的情況有兩種
    a. 直接使用,每當(dāng)父組件重新 render 導(dǎo)致的重傳 props,子組件將直接跟著重新渲染,無論 props 是否有變化。可通過 shouldComponentUpdate 方法優(yōu)化。

    class Child extends Component {
      shouldComponentUpdate(nextProps) {
        // 應(yīng)該使用這個方法,否則無論props是否有變化都將會導(dǎo)致組件跟
        著重新渲染
        if (nextProps.someThings === this.props.someThings) {
          return false
        }
      }
      render() {
        return <div>{this.props.someThings}</div>
      }
    }
    

    b.在 componentWillReceiveProps 方法中,將 props 轉(zhuǎn)換成自己的 state

    class Child extends Component {
      constructor(props) {
        super(props)
        this.state = {
          someThings: props.someThings
        }
      }
      componentWillReceiveProps(nextProps) {
        // 父組件重傳props時就會調(diào)用這個方法
        this.setState({ someThings: nextProps.someThings })
      }
      render() {
        return <div>{this.state.someThings}</div>
      }
    }
    

    根據(jù)官網(wǎng)的描述

    在該函數(shù)(componentWillReceiveProps)中調(diào)用 this.setState() 將不會引起第二次渲染。

    是因為 componentWillReceiveProps 中判斷 props 是否變化了,若變化了,this.setState 將引起 state 變化,從而引起 render,此時就沒必要再做第二次因重傳 props 引起的 render 了,不然重復(fù)做一樣的渲染了。

    • 組件本身調(diào)用 setState,無論 state 有沒有變化。可通過 shouldComponentUpdate 方法優(yōu)化。
    class Child extends Component {
      constructor(props) {
        super(props)
        this.state = {
          someThings: 1
        }
      }
      shouldComponentUpdate(nextStates) {
        // 應(yīng)該使用這個方法,否則無論state是否有變化都將會導(dǎo)致組件
        重新渲染
        if (nextStates.someThings === this.state.someThings) {
          return false
        }
      }
      handleClick = () => {
        // 雖然調(diào)用了setState ,但state并無變化
        const preSomeThings = this.state.someThings
        this.setState({
          someThings: preSomeThings
        })
      }
      render() {
        return <div onClick={this.handleClick}>{this.state.someThings}</div>
      }
    }
    

    此階段分為 componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render,componentDidUpdate

    • componentWillReceiveProps(nextProps)
      此方法只調(diào)用于 props 引起的組件更新過程中,參數(shù) nextProps 是父組件傳給當(dāng)前組件的新 props。但父組件 render 方法的調(diào)用不能保證重傳給當(dāng)前組件的 props 是有變化的,所以在此方法中根據(jù) nextProps 和 this.props 來查明重傳的 props 是否改變,以及如果改變了要執(zhí)行啥,比如根據(jù)新的 props 調(diào)用 this.setState 出發(fā)當(dāng)前組件的重新 render

    • shouldComponentUpdate(nextProps, nextState)
      此方法通過比較 nextProps,nextState 及當(dāng)前組件的 this.props,this.state,返回 true 時當(dāng)前組件將繼續(xù)執(zhí)行更新過程,返回 false 則當(dāng)前組件更新停止,以此可用來減少組件的不必要渲染,優(yōu)化組件性能。

      ps:這邊也可以看出,就算 componentWillReceiveProps()中執(zhí)行了 this.setState,更新了 state,但在 render 前(如 shouldComponentUpdate,componentWillUpdate),this.state 依然指向更新前的 state,不然 nextState 及當(dāng)前組件的 this.state 的對比就一直是 true 了。

    • componentWillUpdate(nextProps, nextState)
      此方法在調(diào)用 render 方法前執(zhí)行,在這邊可執(zhí)行一些組件更新發(fā)生前的工作,一般較少用。

    • render
      render 方法在上文講過,這邊只是重新調(diào)用。

    • componentDidUpdate(prevProps, prevState)
      此方法在組件更新后被調(diào)用,可以操作組件更新的 DOM,prevProps 和 prevState 這兩個參數(shù)指的是組件更新前的 props 和 state

卸載階段

階段只有一個生命周期方法:componentWillUnmount

  • componentWillUnmount
    此方法在組件被卸載前調(diào)用,可以在這里執(zhí)行一些清理工作,比如清楚組件中使用的定時器,清楚 componentDidMount 中手動創(chuàng)建的 DOM 元素等,以避免引起內(nèi)存泄漏。

React v16.4 的生命周期

變更緣由

原來(React v16.0 前)的生命周期在 React v16 推出的Fiber之后就不合適了,因為如果要開啟 async rendering,在 render 函數(shù)之前的所有函數(shù),都有可能被執(zhí)行多次。

原來(React v16.0 前)的生命周期有哪些是在 render 前執(zhí)行的呢?

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

如果開發(fā)者開了 async rendering,而且又在以上這些 render 前執(zhí)行的生命周期方法做 AJAX 請求的話,那 AJAX 將被無謂地多次調(diào)用。。。明顯不是我們期望的結(jié)果。而且在 componentWillMount 里發(fā)起 AJAX,不管多快得到結(jié)果也趕不上首次 render,而且 componentWillMount 在服務(wù)器端渲染也會被調(diào)用到(當(dāng)然,也許這是預(yù)期的結(jié)果),這樣的 IO 操作放在 componentDidMount 里更合適。

禁止不能用比勸導(dǎo)開發(fā)者不要這樣用的效果更好,所以除了 shouldComponentUpdate,其他在 render 函數(shù)之前的所有函數(shù)(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被 getDerivedStateFromProps 替代。

也就是用一個靜態(tài)函數(shù) getDerivedStateFromProps 來取代被 deprecate 的幾個生命周期函數(shù),就是強(qiáng)制開發(fā)者在 render 之前只做無副作用的操作,而且能做的操作局限在根據(jù) props 和 state 決定新的 state

React v16.0 剛推出的時候,是增加了一個 componentDidCatch 生命周期函數(shù),這只是一個增量式修改,完全不影響原有生命周期函數(shù);但是,到了 React v16.3,大改動來了,引入了兩個新的生命周期函數(shù)。

新引入了兩個新的生命周期函數(shù):getDerivedStateFromPropsgetSnapshotBeforeUpdate
getDerivedStateFromProps

static getDerivedStateFromProps(props, state)在組件創(chuàng)建時和更新時的 render 方法之前調(diào)用,它應(yīng)該返回一個對象來更新狀態(tài),或者返回 null 來不更新任何內(nèi)容。

getDerivedStateFromProps 本來(React v16.3 中)是只在創(chuàng)建和更新(由父組件引發(fā)部分),也就是不是由父組件引發(fā),那么 getDerivedStateFromProps 也不會被調(diào)用,如自身 setState 引發(fā)或者 forceUpdate 引發(fā)。

React v16.3

這樣的話理解起來有點亂,在 React v16.4 中改正了這一點,讓 getDerivedStateFromProps 無論是 Mounting 還是 Updating,也無論是因為什么引起的 Updating,全部都會被調(diào)用,具體可看 React v16.4 的生命周期圖。

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate() 被調(diào)用于 render 之后,可以讀取但無法使用 DOM 的時候。它使您的組件可以在可能更改之前從 DOM 捕獲一些信息(例如滾動位置)。此生命周期返回的任何值都將作為參數(shù)傳遞給 componentDidUpdate()。

官網(wǎng)給的例子:

class ScrollingList extends React.Component {
  constructor(props) {
    super(props)
    this.listRef = React.createRef()
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    //我們是否要添加新的 items 到列表?
    // 捕捉滾動位置,以便我們可以稍后調(diào)整滾動.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current
      return list.scrollHeight - list.scrollTop
    }
    return null
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    //如果我們有snapshot值, 我們已經(jīng)添加了 新的items.
    // 調(diào)整滾動以至于這些新的items 不會將舊items推出視圖。
    // (這邊的snapshot是 getSnapshotBeforeUpdate方法的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current
      list.scrollTop = list.scrollHeight - snapshot
    }
  }
  render() {
    return <div ref={this.listRef}>{/* ...contents... */}</div>
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 作為一個合格的開發(fā)者,不要只滿足于編寫了可以運(yùn)行的代碼。而要了解代碼背后的工作原理;不要只滿足于自己的程序...
    六個周閱讀 8,508評論 1 33
  • 40、React 什么是React?React 是一個用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,040評論 0 1
  • 1、什么是react React.js 是一個幫助你構(gòu)建頁面 UI 的庫。React.js 將幫助我們將界面分成了...
    谷子多閱讀 2,567評論 1 13
  • 個人筆記, 轉(zhuǎn)載請注明轉(zhuǎn)載自 szhshp的第三邊境研究所 Refs and the DOM In the t...
    szhielelp閱讀 1,501評論 0 1
  • 3. JSX JSX是對JavaScript語言的一個擴(kuò)展語法, 用于生產(chǎn)React“元素”,建議在描述UI的時候...
    pixels閱讀 2,873評論 0 24