zoukankan      html  css  js  c++  java
  • 4个开发 React 应用的实用技巧

    背景

    Hooks 自推出以来就很火, 它改变了我们编写react 代码的方式, 有助于我们写更简洁的代码。

    今天这边文章不是说Hooks的,Hooks之外, 还有很多实用的技巧可以帮助我们编写简洁清晰的代码。

    今天我就整理了几个使用的技巧,其中有些也是我在公司项目中实践的,现在整理出来分享给大家, 希望对大家有所启发。

    1. 使用字符串来定义一个react元素

    举个简单的例子:

    // 我们可以通过把一个字符串'div' 赋值给一个变量, 就像:
    import React from 'react'
    
    const MyComponent = 'div'
    
    function App() {
      return (
        <>
          <MyComponent>
            <h3>I am inside a {'<div />'} element</h3>
          </MyComponent>
        </>
      )
    }
    

    React 内部会调用 React.createElement, 使用这个字符串来生成这个元素。

    另外, 你也可以显式的定义component 来决定渲染的内容, 比如:

    // 定义一个MyComponent
    function MyComponent({ component: Component = 'div', 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 component="div" name="KK" age={18} email="xxx@gmail.com">
        </>
      )
    }

    这种方式, 你也可以传入一个自定义的组件, 比如:

    function Dashboard({ children }) {
      return (
        <div style={{ padding: '25px 12px' }}>
          {children}
        </div>
      )
    }
    
    function App() {
      return (
        <>
          <MyComponent component={Dashboard} name="KK" age={18} email="xxx@gmail.com">
        </>
      )
    }

    如果你遇到处理一类相似的元素或者组件,可以通过这种自定义的方式抽象出来,简化你的代码。

    举个现实的例子:

    比如我们现在要做一个货物打包的需求, 可以单个打, 也可以批量打, 针对共同点可以写自定义组件:

    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
          {...packRes}
        />
      )
    }
    
    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

    基于以上原因,React 团队引入了Error Boundaries:https://reactjs.org/docs/

    Error boundaries, 其实就是React组件, 你可以用找个组件来处理它捕捉到的任何错误信息。

    当组件树崩溃的时候,也可以显示你自定义的UI,作为回退。

    看 React 官方提供的例子:https://reactjs.org/docs/

    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 />
    </ErrorBoundary>

    Live Demo By Dan Abramov:

    https://codepen.io/gaearon/

    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 style={style} {...this.props} />
        }
      }
      
      return WithBorder
    }
    
    function MyComponent({ style, ...rest }) {
      return (
        <div style={style} {...rest}>
            <h2>
              This is my component and I am expecting some styles.
            </h2>
        </div>
      )
    }
    
    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
            intl={intl}
            translate={(id, values = {}) => { // 注入翻译方法
              if (!id) { return '' }
              return intl.formatMessage(
                typeof id === 'string' ? { id } : id,
                values
              )
            }}
            {...others}
          />
        )
      ), [])
    
      IntlComponent.displayName = `withTranslate(${BaseComponent.displayName || 'BaseComponent'})`
      
      return (
        <Provider store={store}>
          <Intl>
            <IntlComponent
              {...props}
            />
          </Intl>
        </Provider>
      )
    }
    
    export default withTranslate

    用法很灵过:

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

    十分的方便。

    资源搜索网站大全 http://www.szhdn.com 广州VI设计公司https://www.houdianzi.com

    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 (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
            <p>The current mouse position is ({this.state.x}, {this.state.y})</p>
          </div>
        );
      }
    }
    
    class MouseTracker extends React.Component {
      render() {
        return (
          <>
            <h1>移动鼠标!</h1>
            <Mouse />
          </>
        );
      }
    }

    当光标在屏幕上移动时,组件显示其(x,y)坐标。

    现在的问题是:

    我们如何在另一个组件中复用这个行为?

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

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

    我们或许会使用 <Cat mouse={{ x, y }} prop 来告诉组件鼠标的坐标以让它知道图片应该在屏幕哪个位置。

    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 }} />
        );
      }
    }

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

    class Mouse extends React.Component {
      state = { x: 0, y: 0 };
    
      handleMouseMove = (event) => {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }
    
      render() {
        return (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
            <Cat mouse={this.state} />
          </div>
        );
      }
    }

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

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

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

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

    这也是 render prop 的来历:

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

    修改一下上面的代码:

    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 {
      state = { x: 0, y: 0 };
    
      handleMouseMove = (event) => {
        this.setState({
          x: event.clientX,
          y: event.clientY
        });
      }
    
      render() {
        return (
          <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
            {this.props.render(this.state)}
          </div>
        );
      }
    }
    
    class MouseTracker extends React.Component {
      render() {
        return (
          <div>
            <h1>移动鼠标!</h1>
            <Mouse render={mouse => (
              <Cat mouse={mouse} />
            )}/>
          </div>
        );
      }
    }

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

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

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

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

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

    function withMouse(Component) {
      return class extends React.Component {
        render() {
          return (
            <Mouse render={mouse => (
              <Component {...this.props} mouse={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={mouse => ( // 这是不好的! 每个渲染的 `render` prop的值将会是不同的。
              <Cat mouse={mouse} />
            )}/>
          </>
        );
      }
    }

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

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

    class MouseTracker extends React.Component {
      renderTheCat(mouse) {
        return <Cat mouse={mouse} />;
      }
    
      render() {
        return (
          <div>
            <h1>Move the mouse around!</h1>
            <Mouse render={this.renderTheCat} />
          </div>
        );
      }
    }
  • 相关阅读:
    Linux学习之查看是否安装软件
    Linux学习之nfs实例
    Linux学习之nfs安装配置
    Linux 学习之防火墙配置
    Linux学习之系统时间同步
    Linux学习之守护进程详解
    Linux学习之Center os网络配置
    Linux学习之挂载操作
    Linux学习之挂载
    Linux学习之开机启动
  • 原文地址:https://www.cnblogs.com/xiaonian8/p/14019376.html
Copyright © 2011-2022 走看看