[React] Refactor a Class Component with React hooks to a Function

We have a render prop based class component that allows us to make a GraphQL request with a given query string and variables and uses a GitHub graphql client that is in React context to make the request. Let‘s refactor this to a function component that uses the hooks useReducer, useContext, and useEffect.

Class Based Component:

import {Component} from ‘react‘
import PropTypes from ‘prop-types‘
import isEqual from ‘lodash/isEqual‘
import * as GitHub from ‘../../../github-client‘

class Query extends Component {
  static propTypes = {
    query: PropTypes.string.isRequired,
    variables: PropTypes.object,
    children: PropTypes.func.isRequired,
    normalize: PropTypes.func,
  }
  static defaultProps = {
    normalize: data => data,
  }
  static contextType = GitHub.Context

  state = {loaded: false, fetching: false, data: null, error: null}

  componentDidMount() {
    this._isMounted = true
    this.query()
  }

  componentDidUpdate(prevProps) {
    if (
      !isEqual(this.props.query, prevProps.query) ||
      !isEqual(this.props.variables, prevProps.variables)
    ) {
      this.query()
    }
  }

  componentWillUnmount() {
    this._isMounted = false
  }

  query() {
    this.setState({fetching: true})
    const client = this.context
    client
      .request(this.props.query, this.props.variables)
      .then(res =>
        this.safeSetState({
          data: this.props.normalize(res),
          error: null,
          loaded: true,
          fetching: false,
        }),
      )
      .catch(error =>
        this.safeSetState({
          error,
          data: null,
          loaded: false,
          fetching: false,
        }),
      )
  }

  safeSetState(...args) {
    this._isMounted && this.setState(...args)
  }

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

export default Query

Conver props:

 // From
static propTypes = {
    query: PropTypes.string.isRequired,
    variables: PropTypes.object,
    children: PropTypes.func.isRequired,
    normalize: PropTypes.func,
  }
  static defaultProps = {
    normalize: data => data,
  }

// To:

function Query ({query, variables, children, normalize = data => data}) {

}

Conver Context:

// From
static contextType = GitHub.Context
...
const client = this.context

// To:
import {useContext} from ‘react‘

function Query ({query, variables, children, normalize = data => data}) {
  const clinet = useContext(GitHub.Context)
}

Conver State:

I don‘t like to cover each state prop to ‘useState‘ style, it is lots of DRY, instead, using useReducer is a better & clean apporach.

// From
state = {loaded: false, fetching: false, data: null, error: null}

//To:
  import {useContext, useReducer} from ‘react‘
  ...
  const [state, setState] = useReducer(
    (state, newState) => ({...state, ...newState}),
    defaultState)

Conver side effect:

// From:
  componentDidMount() {
    this._isMounted = true
    this.query()
  }

  componentDidUpdate(prevProps) {
    if (
      !isEqual(this.props.query, prevProps.query) ||
      !isEqual(this.props.variables, prevProps.variables)
    ) {
      this.query()
    }
  }

  componentWillUnmount() {
    this._isMounted = false
  }

  query() {
    this.setState({fetching: true})
    const client = this.context
    client
      .request(this.props.query, this.props.variables)
      .then(res =>
        this.safeSetState({
          data: this.props.normalize(res),
          error: null,
          loaded: true,
          fetching: false,
        }),
      )
      .catch(error =>
        this.safeSetState({
          error,
          data: null,
          loaded: false,
          fetching: false,
        }),
      )
  }

// To:

  useEffect(() => {
    setState({fetching: true})
    client
      .request(query, variables)
      .then(res =>
        setState({
          data: normalize(res),
          error: null,
          loaded: true,
          fetching: false,
        }),
      )
      .catch(error =>
        setState({
          error,
          data: null,
          loaded: false,
          fetching: false,
        }),
      )
  }, [query, variables]) // trigger the effects when ‘query‘ or ‘variables‘ changes  

Conver render:

// From:
  render() {
    return this.props.children(this.state)
  }

// To:
function Query({children ... }) {

 ...
 return children(state);
}

-----

Full Code:

import {useContext, useReducer, useEffect} from ‘react‘
import PropTypes from ‘prop-types‘
import isEqual from ‘lodash/isEqual‘
import * as GitHub from ‘../../../github-client‘

function Query ({query, variables, children, normalize = data => data}) {
  const client = useContext(GitHub.Context)
  const defaultState = {loaded: false, fetching: false, data: null, error: null}
  const [state, setState] = useReducer(
    (state, newState) => ({...state, ...newState}),
    defaultState)
  useEffect(() => {
    setState({fetching: true})
    client
      .request(query, variables)
      .then(res =>
        setState({
          data: normalize(res),
          error: null,
          loaded: true,
          fetching: false,
        }),
      )
      .catch(error =>
        setState({
          error,
          data: null,
          loaded: false,
          fetching: false,
        }),
      )
  }, [query, variables]) // trigger the effects when ‘query‘ or ‘variables‘ changes
  return children(state)
}

export default Query

原文地址:https://www.cnblogs.com/Answer1215/p/10393228.html

时间: 2024-08-28 23:12:31

[React] Refactor a Class Component with React hooks to a Function的相关文章

學習 React.js:用 Node 和 React.js 創建一個實時的 Twitter 流

Build A Real-Time Twitter Stream with Node and React.js By Ken Wheeler (@ken_wheeler) 簡介 歡迎來到學習 React 的第二章,該系列文章將集中在怎麼熟練並且有效的使用臉書的 React 庫上.如果你沒有看過第一章,概念和起步,我非常建議你繼續看下去之前,回去看看. 今天我們準備創建用 React 來創建一個應用,通過 Isomorphic Javascript. Iso-啥? Isomorphic. Java

[React] Refactor a Stateful List Component to a Functional Component with React PowerPlug

In this lesson we'll look at React PowerPlug's <List /> component by refactoring a normal class component with state and handlers to a functional component powered by React PowerPlug. import React from "react"; import { render } from "

React.createClass和extends Component的区别

React.createClass和extends Component的区别主要在于: 语法区别 propType 和 getDefaultProps 状态的区别 this区别 Mixins 语法区别 React.createClass import React from 'react'; const Contacts = React.createClass({ render() { return ( <div></div> ); } }); export default Cont

[React Router] Prevent Navigation with the React Router Prompt Component

In this lesson we'll show how to setup the Prompt component from React Router. We'll prompt with a static message, as well as a dynamic method with the message as a function. Finally we'll show that you can return true from the message as a function

React.Component 与 React.PureComponent(React之性能优化)

前言 先说说 shouldComponentUpdate 提起React.PureComponent,我们还要从一个生命周期函数 shouldComponentUpdate 说起,从函数名字我们就能看出来,这个函数是用来控制组件是否应该被更新的. React.PureComponent 通过prop和state的浅对比来实现shouldComponentUpate(). 简单来说,这个生命周期函数返回一个布尔值. 如果返回true,那么当props或state改变的时候进行更新: 如果返回fal

React 的 PureComponent Vs Component

一.它们几乎完全相同,但是PureComponent通过prop和state的浅比较来实现shouldComponentUpdate,某些情况下可以用PureComponent提升性能 1.所谓浅比较(shallowEqual),即react源码中的一个函数,然后根据下面的方法进行是不是PureComponent的判断,帮我们做了本来应该我们在shouldComponentUpdate中做的事情 if (this._compositeType === CompositeTypes.PureCla

[React Native] Animate Styles of a React Native View with Animated.timing

In this lesson we will use Animated.timing to animate the opacity and height of a View in our React Native application. This function has attributes that you can set such as easing and duration. import React, {Component} from 'react'; import {Text, V

react.js 从零开始(七)React (虚拟)DOM

React 元素 React 中最主要的类型就是 ReactElement.它有四个属性:type,props,key 和ref.它没有方法,并且原型上什么都没有. 可以通过 React.createElement 创建该类型的一个实例. var root = React.createElement('div'); 为了渲染一个新的树形结构到 DOM 中,你创建若干个 ReactElement,然后传给React.render 作为第一个参数,同时将第二个参数设为一个正规的 DOM 元素(HTM

我的 React Native 技能树点亮计划 の React Native 开发 IDE 选型和配置

@author ASCE1885的 Github 简书 微博 CSDN 知乎 本文首发于 InfoQ 移动技术公众号:移动开发前线 由于潜在的商业目的,未经许可不开放全文转载许可,谢谢! React Native 发布一年多了,有不少公司已经在线上产品中或小范围试水,或大范围应用,很多公司或开发者都在为 React Native 的生态系统作出自己的贡献.React Native 的开发基本上是 Javascript + 系统原生开发语言(Java,Objective-C,Swift),原生语言