學習React Advanced Guides

基礎概覽

state 的使用

  • React可以將多個 setState()調用合併成一個調用來提高性能

  • 狀態更新可能是異步的

  • 不能依靠 this.propsthis.state 計算下一個狀態

  • setState的第二種形式

    this.setState((prevState, props) => {
        // 先前的狀態 prevState
        // 此次更新被應用的 props
    })

     

事件處理

  • arrow functionsFunction.prototype.bind 兩種爲事件處理函數傳遞參數

    • arrow functions 使用這種語法的問題是每次該組件渲染時都會創建一個不同的回調函數

      • 如果該回調函數作爲屬性值傳入低階組件,這些組件可能會進行額外的重新渲染

列表 & keys

  • 如果列表項目的順序可能會變化,我們不建議使用索引來用作鍵值,因爲這樣做會導致性能的負面影響,還可能引起組件狀態問題。

狀態提升

  • 使用 react 經常會遇到幾個組件需要共用狀態數據的情況。這種情況下,我們最好將這部分共享的狀態提升至他們最近的父組件當中進行管理。

定義UI狀態的最小表示

  • 找出 state

    1. 它是通過 props 從父級傳來的嗎?如果是,他可能不是 state。

    2. 它隨着時間推移不變嗎?如果是,它可能不是 state。

    3. 你能夠根據組件中任何其他的 state 或 props 把它計算出來嗎?如果是,它不是 state。

  • 確定 state 的位置

    1. 確定每一個需要這個 state 來渲染的組件

    2. 找到一個公共所有者組件

    3. 公共所有者擁有這個 state

進階指南

Context

  • React組件通過props傳遞數據,是自頂向下的

  • context可以傳遞數據通過組件樹,而不用向每個層級層層傳遞props

    • 好處: 避免層層傳遞props,底層才能拿到數據

    • 如果只想避免通過許多級別傳遞一些屬性,那麼組件組合通常比上下文更簡單

    • 謹慎的使用context,它可能會使組件複用變得困難

  • 提供四個API

    • React.createContext

    • Context.Provider

    • Class.contextType

    • Context.Consumer

  • Context 簡單使用

    // light 爲 defaultValue 僅在當一個組件在組件樹中沒有匹配到the closest Provider
    // 可以 傳屬性、方法 
    const ThemeContext = React.createContext('light');
    class App extends React.Component {
        render() {
        // Use a Provider to pass the current theme to the tree below.
        // Any component can read it, no matter how deep it is.
        // In this example, we're passing "dark" as the current value.
        return (
          <ThemeContext.Provider value="dark">
            <Toolbar />
          </ThemeContext.Provider>
        );
      }
    }
    ​
    // A component in the middle doesn't have to
    // pass the theme down explicitly anymore.
    function Toolbar(props) {
      return (
        <div>
          <ThemedButton />
        </div>
      );
    }
    // Consumer兩種寫法
    class ThemedButton extends React.Component {
      // Assign a contextType to read the current theme context.
      // React will find the closest theme Provider above and use its value.
      // In this example, the current theme is "dark".
      static contextType = ThemeContext;
      render() {
        return <Button theme={this.context} />;
      }
    }
    class ThemedButton extends React.Component {
        render() {
            return (
                <ThemeContext.Consumer>
                    {value => (
                        <Button theme={value} />
                    )}
                </ThemeContext.Consumer>
            );
        }
    }
  • 一個生產者多個消費者

    • 1 Context.Provider

    • n Context.Consume

  • Provider 值屬性改變時,Consume將重新render。從Provider到Consume的傳播不受shouldComponentUpdate方法約束。

  • Consume 可以通過 static contextType = ThemeContext ,然後使用 this.context獲取到the closest Provider的value值

  • Consume 可以使用

    <Context.Consumer>
        {value => /* render something based on the context value */}
    </Context.Consumer>
  • Consuming Multiple Contexts

    • 多層Provider ,多層Consumer 嵌套

Error Boundaries

  • Error boundaries do not catch errors for:

    • Event handlers (learn more)

    • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)

    • Server side rendering

    • Errors thrown in the error boundary itself (rather than its children)

  • A class component becomes an error boundary if it defines either (or both) of the lifecycle methods static getDerivedStateFromError() or componentDidCatch().

    • Use static getDerivedStateFromError() to render a fallback UI after an error has been thrown. Use componentDidCatch() to log error information.

  • Error Boundaries 簡單使用

    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    ​
      static getDerivedStateFromError(error) {
        // Update state so the next render will show the fallback UI.
        return { hasError: true };
      }
    ​
      componentDidCatch(error, info) {
        // You can also log the error to an error reporting service
        logErrorToMyService(error, info);
      }
    ​
      render() {
        if (this.state.hasError) {
          // You can render any custom fallback UI
          return <h1>Something went wrong.</h1>;
        }
    ​
        return this.props.children; 
      }
    }

Forwarding Refs

  • API React.forwardRef

    const FancyButton = React.forwardRef((props, ref) => (
      <button ref={ref} className="FancyButton">
        {props.children}
      </button>
    ));
    ​
    // You can now get a ref directly to the DOM button:
    // 此處便獲取了 button 的 引用
    const ref = React.createRef();
    <FancyButton ref={ref}>Click me!</FancyButton>;
  • 使父組件可以獲取子組件的引用,即可獲取子組件實體,調用子組件的方法

Higher-Order Components

  • 複用組件邏輯時使用

  • 不要在 render 方法中使用 HOCs ,當更新時,會完整的卸載之前的組件樹

Portals

  • API ReactDOM.createPortal(child, container)

  • 使用場景 a parent component has an overflow: hidden or z-index style, but you need the child to visually “break out” of its container. For example, dialogs, hovercards, and tooltips.

  • 事件冒泡能通過 portals 繼續向上

Reconciliation

  • 主要解釋了 React’s “diffing” algorithm

    • Elements Of Different Types 元素類型不同,銷燬舊的構建新的

    • DOM Elements Of The Same Type 元素類型相同,比較元素的屬性,屬性不同只更新屬性

    • Component Elements Of The Same Type 組件元素類型相同,React更新props以匹配新元素,並調用componentWillReceiveProps()andcomponentWillUpdate()

Refs and the DOM

  • Refs 提供一種能夠獲取Dom節點或者組件的方法

    • 經典的React數據流:props是父組件影響子組件的唯一方式,爲了修改子組件,需要用新的props re-render子組件

  • 什麼時候使用

    • Managing focus, text selection, or media playback. 獲取焦點,文本選擇,媒體的播放

    • Triggering imperative animations. 觸發動畫

    • Integrating with third-party DOM libraries. 與第三方dom庫整合

  • 可以被聲明式的寫時 避免使用ref

  • 不能使用ref在function components,因爲它沒有實例

  • 生命週期中 ref的狀況

    • 當組件 mounts時,React將current property分配給 dom元素

    • 當組件 unmounts時, 返回 null

    • Ref updates 在componentDidMountorcomponentDidUpdate 之前

Render Props

  • 複用 狀態和行爲 給其它需要相同狀態和行爲的組件

  • 例子,鼠標移動時,分享座標值給各個組件

    class Cat extends React.Component {
      render() {
        const mouse = this.props.mouse;
        return (
          <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
        );
      }
    }
    class Mouse extends React.Component {
      constructor(props) {
        super(props);
        this.handleMouseMove = this.handleMouseMove.bind(this);
        this.state = { x: 0, y: 0 };
      }
    ​
      handleMouseMove(event) {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }
    ​
      render() {
        return (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
    ​
            {/* ...but how do we render something other than a <p>? */}
            this.props.children(this.state)
          </div>
        );
      }
    }
    ​
    class MouseTracker extends React.Component {
      render() {
        return (
          <div>
            <h1>Move the mouse around!</h1>
            {/* children prop doesn’t actually need to be named in the list of “attributes” in your JSX element */}
            <Mouse>
             {mouse => <Cat mouse={mouse} />}    
            </Mouse>
          </div>
        );
      }
    }
    ​
    // 也可以使用 HOC
    function withMouse(Component){
        return class extends React.Component {
            render(){
                return (
                    <Mouse>
                        {mouse => <Component {...this.props} mouse={mouse} />}
                    </Mouse>
                )
            }
        }
    }
    ​

     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章