首頁  >  文章  >  web前端  >  4個開發React應用的實用技巧

4個開發React應用的實用技巧

青灯夜游
青灯夜游轉載
2019-11-26 18:08:034335瀏覽

4個開發React應用的實用技巧

【相關專題推薦:react面試題(2020)】

背景

Hooks 自推出以來就很火, 它改變了我們編寫React 程式碼的方式, 有助於我們寫更簡潔的程式碼。

今天這邊文章不是說Hooks的,Hooks之外, 還有很多實用的技巧可以幫助我們寫出簡潔清晰的程式碼。

今天我就整理了幾個使用的技巧,其中有些也是我在公司專案中實踐的,現在整理出來分享給大家, 希望對大家有所啟發

正文

#1. 使用字串來定義一個React元素

#舉個簡單的例子:

// 我们可以通过把一个字符串'p' 赋值给一个变量, 就像:
import React from 'react'

const MyComponent = 'p'

function App() {
  return (
    
      <mycomponent>
        <h3>I am inside a {'</h3>
<p></p>'} element
      </mycomponent>
    >
  )
}

React 內部會呼叫React.createElement, 使用這個字串來產生這個元素。

另外, 你也可以明確的定義component 來決定渲染的內容, 例如:

// 定义一个MyComponent
function MyComponent({ component: Component = 'p', name, age, email }) {
  
  return (
    <component>
      <h1>Hi {name} </h1>
      
        <h6>You are {age} years old</h6>
        <small>Your email is {email}</small>
      >
    </component>
  )
}

適用方式:

function App() {
  return (
    
      <mycomponent>
    >
  )
}</mycomponent>

這種方式, 你也可以傳入一個自訂的元件, 例如:

function Dashboard({ children }) {
  return (
    

      {children}     

  ) } function App() {   return (            <mycomponent>     >   ) }</mycomponent>

如果你遇到處理一類相似的元素或元件,可以透過這種自訂的方式抽像出來,簡化你的程式碼。

舉個現實的例子:

例如我們現在要做一個貨物打包的需求, 可以單一打, 也可以批量打, 針對共同點可以寫自訂元件:

import React from 'react'
import withTranslate from '@components/withTranslate'
import PackComponent from './PackComponent'
import usePack, { check } from './usePack'

let PackEditor = (props) => {
  const packRes = usePack(props)
  return (
    <packcomponent></packcomponent>
  )
}

PackEditor = withTranslate(PackEditor)
PackEditor.check = check

export default PackEditor

這樣在不同的業務模組中, 就可以靈活的使用了, 非常方便。

2. 定義錯誤邊界

在Javascript裡,我們都是使用try/catch 來捕捉可能發生的異常,在catch中處理錯誤。例如:

function getFromLocalStorage(key, value) {
  try {
    const data = window.localStorage.get(key)
    return JSON.parse(data)
  } catch (error) {
    console.error
  }
}

這樣, 即使發生了錯誤, 我們的應用也不至於崩潰白螢幕。

React 歸根究底也是Javascript,本質上沒什麼不同, 所以同樣的使用try/catch  也沒問題。

然而, 由於React 實作機制的原因, 發生在元件內部的Javascript 錯誤會破壞內部狀態, render會產生錯誤:

https://github.com/facebook/react/ issues/4026

4個開發React應用的實用技巧

基於以上原因,React 團隊引入了Error Boundaries:

https://reactjs.org/docs /error-boundaries.html

Error boundaries, 其實就是React元件, 你可以用找個元件來處理它捕捉到的任何錯誤訊息。

當元件樹崩潰的時候,也可以顯示你自訂的UI,作為回退。

看React 官方提供的範例:
https://reactjs.org/docs/error-boundaries.html#introducing-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, errorInfo) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, errorInfo)
  }
  
  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>
    }
    return this.props.children
  }
}

使用方式:

<errorboundary>
  <mywidget></mywidget>
</errorboundary>

Live Demo By Dan Abramov:

https://codepen.io/gaearon/pen/wqvxGa?editors=0010

3.高階元件

通俗點講, 所謂高階元件就是, 你丟一個元件進去, 增加一些屬性或操作, 再丟出來。

一般來說, 你可以把一些具備共同點的元件抽象化成一個高階元件, 然後再不同的模組中重複使用

例如, 我們的系統中, 有一類按鈕要加個border, 很多地方都要用到, 我們把它抽像出來:

import React from 'react'

// Higher order component
const withBorder = (Component, customStyle) => {
  class WithBorder extends React.Component {
    render() {
      const style = {
        border: this.props.customStyle ? this.props.customStyle.border : '3px solid teal'
      }
      return <component></component>
    }
  }
  
  return WithBorder
}

function MyComponent({ style, ...rest }) {
  return (
    <p>
        </p><h2>
          This is my component and I am expecting some styles.
        </h2>
    
  )
}

export default withBorder(MyComponent, { border: '4px solid teal' })

經過withBorder裝飾的MyComponent元件, 就具備了統一border這項功能, 後面如果如果要做修改, 就可以在這個中間層統一處理, 非常方便。

在我的專案裡, 也用了一些高階元件, 舉個具體的例子:

PackEditor = withTranslate(PackEditor)

我們的這個PackEditor 就是一個增強過的元件,增加了什麼功能呢?

如同名字表達的, withTranslate, 增加了一個翻譯功能, 下面也給大家看看這個元件是怎麼實現的:

import React from 'react'
import { Provider } from 'react-redux'
import { injectIntl } from 'react-intl'
import { store } from '@redux/store'
import { Intl } from './Locale'

const withTranslate = BaseComponent => (props) => {
  // avoid create a new component on re-render
  const IntlComponent = React.useMemo(() => injectIntl(
    ({ intl, ...others }) => (
      <basecomponent> { // 注入翻译方法
          if (!id) { return '' }
          return intl.formatMessage(
            typeof id === 'string' ? { id } : id,
            values
          )
        }}
        {...others}
      />
    )
  ), [])

  IntlComponent.displayName = `withTranslate(${BaseComponent.displayName || 'BaseComponent'})`
  
  return (
    <provider>
      <intl>
        <intlcomponent></intlcomponent>
      </intl>
    </provider>
  )
}

export default withTranslate</basecomponent>

用法很靈過:

const Editor = withTranslate(({
  // ...
  translate,
}) => {
  // ...
   return (
     
      {translate('xxx')}}
     >
   )
})

十分的方便。

4. Render props

Rrender prop 是指一種在React 元件之間使用一個值為函數的prop 共享代碼的簡單技術, 和HOC 類似,都是組件間的邏輯復用問題

更具體地說,Render prop 是一個用來告知元件需要渲染什麼內容的函數。

下面看一下簡單的例子:

以下元件追蹤Web 應用程式中的滑鼠位置:

class Mouse extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <p>
        </p><p>The current mouse position is ({this.state.x}, {this.state.y})</p>
      
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      
        <h1>移动鼠标!</h1>
        <mouse></mouse>
      >
    );
  }
}

當遊標在螢幕上移動時,元件顯示其(x ,y)座標。

現在的問題是:

我們如何在另一個元件中重複使用這個行為?

换个说法,若另一个组件需要知道鼠标位置,我们能否封装这一行为,以便轻松地与其他组件共享它 ??

假设产品想要这样一个功能: 在屏幕上呈现一张在屏幕上追逐鼠标的猫的图片。

我们或许会使用

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img  alt="4個開發React應用的實用技巧" >
    );
  }
}

这个需求如此简单,你可能就直接修改Mouse组件了:

class Mouse extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      <p>
        <cat></cat>
      </p>
    );
  }
}

巴适~ 简单粗暴, 一分钟完成任务。

可是,如果下次产品再要想加条狗呢

以上的例子,虽然可以完成了猫追鼠标的需求,还没有达到以可复用的方式真正封装行为的目标。

当我们想要鼠标位置用于不同的用例时,我们必须创建一个新的组件,专门为该用例呈现一些东西.

这也是 render prop 的来历:

我们可以提供一个带有函数 prop 的 <mouse></mouse> 组件,它能够动态决定什么需要渲染的,而不是将 硬编码 组件里.

修改一下上面的代码:

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img  alt="4個開發React應用的實用技巧" >
    );
  }
}

class Mouse extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  }

  render() {
    return (
      

        {this.props.render(this.state)}       

    );   } } class MouseTracker extends React.Component {   render() {     return (       

        

移动鼠标!

         (                    )}/>       

    );   } }

提供了一个render 方法,让动态决定什么需要渲染。

事实上,render prop 是因为模式才被称为 render prop ,不一定要用名为 render 的 prop 来使用这种模式。

任何被用于告知组件需要渲染什么内容的函数 prop, 在技术上都可以被称为 "render prop".

另外,关于 render prop 一个有趣的事情是你可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。

例如,如果你更喜欢使用 withMouse HOC 而不是 组件,你可以使用带有 render prop 的常规 轻松创建一个:

function withMouse(Component) {
  return class extends React.Component {
    render() {
      return (
        <mouse> (
          <component></component>
        )}/>
      );
    }
  }
}</mouse>

也是非常的简洁清晰。

有一点需要注意的是, 如果你在定义的render函数里创建函数, 使用 render prop 会抵消使用 React.PureComponent 带来的优势。

因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值

class Mouse extends React.PureComponent {
  // 与上面相同的代码......
}

class MouseTracker extends React.Component {
  render() {
    return (
      
        <mouse> ( // 这是不好的! 每个渲染的 `render` prop的值将会是不同的。
          <cat></cat>
        )}/>
      >
    );
  }
}</mouse>

在这样例子中,每次 渲染,它会生成一个新的函数作为 的 prop,因而在同时也抵消了继承自 React.PureComponent 的 组件的效果.

为了绕过这一问题,有时你可以定义一个 prop 作为实例方法,类似这样:

class MouseTracker extends React.Component {
  renderTheCat(mouse) {
    return <cat></cat>;
  }

  render() {
    return (
      <p>
        </p><h1>Move the mouse around!</h1>
        <mouse></mouse>
      
    );
  }
}

5.组件性能

性能优化是永恒的主题, 这里不一一细说, 提供积分资源供你参考:

总结

以上几点都是我们经常要使用的技巧, 简单实用, 分享给大家, 希望能给大家带来一些帮助或启发,谢谢。

推荐阅读:React在线手册

以上是4個開發React應用的實用技巧的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除