React冷門但很好用的知識點

介紹

最近在重讀React官方文檔,盤點一些不經常使用但有用的知識點。若是有啥說的很差的地方,歡迎指正!html

推薦個翻譯的不錯的React文檔react

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate()爲React生命週期函數, 在render()以前調用。它使得組件能在發生更改以前從 DOM 中捕獲一些信息(例如,滾動位置)。今生命週期的任何返回值將做爲參數傳遞給 componentDidUpdate()dom

處理聊天滾動的示例:ide

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 咱們是否在 list 中添加新的 items ?
    // 捕獲滾動位置以便咱們稍後調整滾動位置。
    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 有值,說明咱們剛剛添加了新的 items,
    // 調整滾動位置使得這些新 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>
    );
  }
}
複製代碼

forceUpdate()

默認狀況下,當組件的 stateprops 發生變化時,組件將從新渲染。若是 render() 方法依賴於其餘數據,則能夠調用 forceUpdate() 強制讓組件從新渲染。函數

調用 forceUpdate() 將導致組件調用 render() 方法,此操做會跳過該組件的 shouldComponentUpdate()。但其子組件會觸發正常的生命週期方法,包括 shouldComponentUpdate() 方法。若是標記發生變化,React 仍將只更新 DOM。this

簡單來講forceUpdate()方法能夠強制更新組件,因此不建議使用!spa

key

key顧名思義爲組件的標識,當key改變時組件會從新註冊!但咱們使用最多的仍是遍歷!翻譯

const todos = [1, 2, 3, 4, 5];
const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
複製代碼

其實用index做爲Key有問題,能夠看這篇博客code

狀態提高

官方解釋:一般,多個組件須要反映相同的變化數據,這時咱們建議將共享狀態提高到最近的共同父組件中去。component

簡單來講,就是兩個子組件都使用父組件的一個參數,經過props把改參數傳入子組件,並把改變該參數的方法一併傳入子組件,經過調用該方法改變父組件共用的參數!

舉個例子🌰

如今有ComponentA以及ComponentB,他們各有一個輸入框,當我在其中一個輸入框輸入英文單詞時,在另外一個組件展現轉換後的大小寫。

class ComponentA extends React.Component {
  // 當input輸入時,在另外一個組件顯示大的大小寫轉換
  handleChange(e) => {
    const { onChangeWordUpper } = this.props;
    onChangeWordUpper(e.target.value);
  };

  render() {
    const { word } = this.props;
    return (
      <h2>ComponentA enter: {word}</h2>
      <input onChange={this.handleChange} />
    );
  }
}
複製代碼
class ComponentB extends React.Component {
  // 當input輸入時,在另外一個組件顯示大的大小寫轉換
  handleChange(e) => {
    const { onChangeWordLower } = this.props;
    onChangeWordLower(e.target.value);
  };

  render() {
    const { word } = this.props;
    return (
      <h2>ComponentB enter: {word}</h2>
      <input onChange={this.handleChange} />
    );
  }
}
複製代碼

父組件componentFather

class ComponentFather extends React.Component {
  state = {
    word: '';
  }

  // 轉換爲大寫
  onChangeWordUpper(value) => {
    const word = value.toUpperCase()
    this.setState({ word });
  }
  
  // 轉換爲小寫
  onChangeWordLower(value) => {
    const word = value.toLowerCase()()
    this.setState({ word });
  }

  render() {
    const { word } = this.state;
    const componentAProps = {
      word,
      onChangeWordUpper: this.onChangeWordUpper,
    };
    const componentBProps = {
      word,
      onChangeWordUpper: this.onChangeWordLower,
    };
    return (
      <ComponentA {...componentAProps} />
      <ComponentB {...componentBProps} />
    );
  }
}
複製代碼

Fragments

Fragments你們應該都很熟悉!

React 中的一個常見模式是一個組件返回多個元素。Fragments 容許你將子列表分組,而無需向 DOM 添加額外節點

<React.Fragment>
  <div>somethings</div>
  <div>somethings</div>
</React.Fragment>
複製代碼

能夠簡寫爲

<>
  <div>somethings</div>
  <div>somethings</div>
</>
複製代碼

children prop

當一個組件沒法知曉子組件的內容時,children prop就頗有用。

它能夠把子組件整個傳遞下去!

舉個例子🌰

// 父組件
function Header(props) {
  return (
    <div> {props.children} </div>
  );
}

function Page() {
  return (
    <Header> <h1>children - 1</h1> <h1>children - 2</h1> </Header>
  );
}
複製代碼

這樣Header組件接受的children就是兩個h1標籤

Context

Context應該是很經常使用的Api,防止有人沒使用過並鞏固一下個人知識點,仍是寫一下。

舉個例子🌰

import React, { createContext } from "react";
import ReactDOM from "react-dom";

const ThemeContext = createContext("red");

class App extends React.Component {
  state = {
    color: "green"
  };

  handleChangeColor = () => {
    if(this.state.color === 'green') {
      this.setState({ color: "red" });
    } else {
      this.setState({ color: "green" });
    }
  };

  render() {
    // 使用一個 Provider 來將當前的 color 傳遞給如下的組件樹。
    // 不管多深,任何組件都能讀取這個值。
    // 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
    return (
      <ThemeContext.Provider value={this.state.color}>
        <Toolbar />
        <br />
        <button onClick={this.handleChangeColor}>changeColor</button>
      </ThemeContext.Provider>
    );
  }
}

// 中間的組件不再必指明往下傳遞 color 了。
function Toolbar(props) {
  return <ThemedSpan />;
}

class ThemedSpan extends React.Component {
  // 指定 contextType 讀取當前的 color context。
  // React 會往上找到最近的 color Provider,而後使用它的值。
  // 在這個例子中,當前的 color 值爲 「green」。
  static contextType = ThemeContext;
  render() {
    return <Span color={this.context} />;
  }
}

class Span extends React.Component {
  static contextType = ThemeContext;
  render() {
    return (
      <span style={{ backgroundColor: this.props.color }}>{this.context}</span>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));

複製代碼

注意事項:Provider的value我放在了state裏。這樣當value更新時不會致使其餘組件渲染。

假設,value傳入的是一個對象。

// bad
const obj = {a: 1};
<ThemeContext.Provider value={obj}> <Toolbar /> </ThemeContext.Provider> 複製代碼

當obj改變,但obj.a 仍是 1,實際上,a的value並無改變,可是obj !== obj。

可是this.state.obj === this.state.obj

// good
<ThemeContext.Provider value={this.state.obj}>
  <Toolbar /> </ThemeContext.Provider> 複製代碼

codesandbox地址

未完待續

相關文章
相關標籤/搜索