React Hooks介绍及应用场景

栏目: IOS · Android · 发布时间: 6年前

内容简介:在使用React开发相关页面或维护相关组件时,常常会遇到一些难以避免的问题,比如:为了解决这类问题,目前常见的解决方法为render props 与基于上述原因,react团队在React

引言 & 背景

在使用React开发相关页面或维护相关组件时,常常会遇到一些难以避免的问题,比如:

  1. 组件中与状态相关的逻辑很难复用,例如相似弹层组件的打开、关闭、loading等
  2. 逻辑复杂的组件 难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面。
  3. class组件中的this增加学习成本,class组件在基于现有 工具 的优化上存在些许问题。
  4. 由于业务变动,函数组件不得不改为类组件等等。

为了解决这类问题,目前常见的解决方法为render props 与 higher-order components ,但这两种方式可能会很笨重,而且会导致JSX嵌套过深。

// 普通方法
import React from 'react'
import ReactDOM from 'react-dom'

const App = React.createClass({
  getInitialState() {
    return { x: 0, y: 0 }
  },

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

  render() {
    const { x, y } = this.state

    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

ReactDOM.render(<App/>, document.getElementById('app'))
复制代码
// mixin
// 缺点ES6 classes,state修改来源很难追踪,多个mixin名称冲突
import React from 'react'
import ReactDOM from 'react-dom'

// This mixin contains the boilerplate code that
// you'd need in any app that tracks the mouse position.
// We can put it in a mixin so we can easily share
// this code with other components!
const MouseMixin = {
  getInitialState() {
    return { x: 0, y: 0 }
  },

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

const App = React.createClass({
  // Use the mixin!
  mixins: [ MouseMixin ],

  render() {
    const { x, y } = this.state

    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

ReactDOM.render(<App/>, document.getElementById('app'))
复制代码
// HOC
// 缺点JSX嵌套,props来源很难确定,多个HOC导致props冲突以及props传递问题
const withMouse = (Component) => {
  return class 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}>
          <Component {...this.props} mouse={this.state}/>
        </div>
      )
    }
  }
}

const App = React.createClass({
  render() {
    // Instead of maintaining our own state,
    // we get the mouse position as a prop!
    const { x, y } = this.props.mouse

    return (
      <div style={{ height: '100%' }}>
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

// Just wrap your component in withMouse and
// it'll get the mouse prop!
const AppWithMouse = withMouse(App)
复制代码
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

// Instead of using a HOC, we can share code using a
// regular component with a render prop!
class Mouse extends React.Component {
  static propTypes = {
    render: PropTypes.func.isRequired
  }

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

const App = React.createClass({
  render() {
    return (
      <div style={{ height: '100%' }}>
        <Mouse render={({ x, y }) => (
          // The render prop gives us the state we need
          // to render whatever we want here.
          <h1>The mouse position is ({x}, {y})</h1>
        )}/>
      </div>
    )
  }
})

ReactDOM.render(<App/>, document.getElementById('app'))

// render props -> hoc
const withMouse = (Component) => {
  return class extends React.Component {
    render() {
      return <Mouse render={mouse => (
        <Component {...this.props} mouse={mouse}/>
      )}/>
    }
  }
}
复制代码

React Hooks概述

基于上述原因,react团队在React 16.7.0-alpha 推出了React Hooks这一新的特性。

首先用一段简单的代码介绍一下什么是React Hooks

import { useState, useEffect } from 'react';

function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);
  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

复制代码

什么是React Hooks? React Hooks是一种React中使用的特殊的函数,例如**useState**可以使我们在React函数组件中使用state,**useEffect**可以使我们在函数式组件中处理副作用。

React Hooks 带来的好处不仅是 “更 FP,更新粒度更细,代码更清晰”,还有如下三个特性:

render

React Hooks生命周期

useState 什么时候执行? 它会在组件每次render的时候调用

useEffect 什么时候执行? 它会在组件 mount 和 unmount 以及每次重新渲染的时候都会执行,也就是会在 componentDidMount、componentDidUpdate、componentWillUnmount 这三个时期执行。

清理函数(clean up)什么时候执行? 它会在前一次 effect执行后,下一次 effect 将要执行前,以及 Unmount 时期执行

React Hooks 组合与自定义Hooks

import { useState, useEffect } from "react";

// 底层 Hooks, 返回布尔值:是否在线
function useFriendStatusBoolean(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

// 上层 Hooks,根据在线状态返回字符串:Loading... or Online or Offline
function useFriendStatusString(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);

  if (isOnline === null) {
    return "Loading...";
  }
  return isOnline ? "Online" : "Offline";
}

// 使用了底层 Hooks 的 UI
function FriendListItem(props) {
  const isOnline = useFriendStatusBoolean(props.friend.id);

  return (
    <li style={{ color: isOnline ? "green" : "black" }}>{props.friend.name}</li>
  );
}

// 使用了上层 Hooks 的 UI
function FriendListStatus(props) {
  const statu = useFriendStatusString(props.friend.id);

  return <li>{statu}</li>;
}
复制代码

React Hooks 应用举例

利用 useState 创建 Redux

// 这就是 Redux
function useReducer(reducer, initialState) {
  const [state, setState] = useState(initialState);

  function dispatch(action) {
    const nextState = reducer(state, action);
    setState(nextState);
  }

  return [state, dispatch];
}
// 一个 Action
function useTodos() {
  const [todos, dispatch] = useReducer(todosReducer, []);

  function handleAddClick(text) {
    dispatch({ type: "add", text });
  }

  return [todos, { handleAddClick }];
}

// 绑定 Todos 的 UI
function TodosUI() {
  const [todos, actions] = useTodos();
  return (
    <>
      {todos.map((todo, index) => (
        <div>{todo.text}</div>
      ))}
      <button onClick={actions.handleAddClick}>Add Todo</button>
    </>
  );
}
复制代码

利用 hooks取代生命周期

// react调用g2
class Component extends React.PureComponent<Props, State> {
  private chart: G2.Chart = null;
  private rootDomRef: React.ReactInstance = null;

  componentDidMount() {
    this.rootDom = ReactDOM.findDOMNode(this.rootDomRef) as HTMLDivElement;

    this.chart = new G2.Chart({
      container: document.getElementById("chart"),
      forceFit: true,
      height: 300
    });
    this.freshChart(this.props);
  }

  componentWillReceiveProps(nextProps: Props) {
    this.freshChart(nextProps);
  }

  componentWillUnmount() {
    this.chart.destroy();
  }

  freshChart(props: Props) {
    // do something
    this.chart.render();
  }

  render() {
    return <div ref={ref => (this.rootDomRef = ref)} />;
  }
}
// hooks调用g2
function App() {
  const ref = React.useRef(null);
  let chart: G2.Chart = null;

  React.useEffect(() => {
    if (!chart) {
      chart = new G2.Chart({
        container: ReactDOM.findDOMNode(ref.current) as HTMLDivElement,
        width: 500,
        height: 500
      });
    }

    // do something
    chart.render();

    return () => chart.destroy();
  });

  return <div ref={ref} />;
}
复制代码

修改页面 title

function useDocumentTitle(title) {
  useEffect(
    () => {
      document.title = title;
      return () => (document.title = "大宝");
    },
    [title]
  );
}
// 使用
useDocumentTitle("包裹复核");
复制代码

监听页面大小变化

function getSize() {
  return {
    innerHeight: window.innerHeight,
    innerWidth: window.innerWidth,
    outerHeight: window.outerHeight,
    outerWidth: window.outerWidth
  };
}

function useWindowSize() {
  let [windowSize, setWindowSize] = useState(getSize());

  function handleResize() {
    setWindowSize(getSize());
  }

  useEffect(() => {
    window.addEventListener("resize", handleResize);
    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return windowSize;
}
// 使用
const windowSize = useWindowSize();
return <div>页面高度:{windowSize.innerWidth}</div>;
复制代码

其他

  • DOM 副作用修改 / 监听。
  • React组件辅助函数。
  • 处理动画相关逻辑。
  • 处理发送请求。
  • 处理表单填写。
  • 存数据。

注意

  • 只能在顶层代码(Top Level)中调用 Hook
  • 不要在循环,条件判断,嵌套函数里面调用 Hooks
  • 只在 React 的函数里面调用 Hooks
  • 命名时使用use*命名Hooks
  • 使用 eslint-plugin-react-hooks 插件进行检查

参考文档

  1. React官方文档
  2. 精读《React Hooks》
  3. 精读《怎么用 React Hooks 造轮子》
  4. 10分钟了解 react 引入的 Hooks
  5. [ 译] React hooks: 不是魔法,只是数组
  6. Use a Render Prop!

码字不易,如有建议请扫码 React Hooks介绍及应用场景


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

R Cookbook

R Cookbook

Paul Teetor / O'Reilly Media / 2011-3-22 / USD 39.99

With more than 200 practical recipes, this book helps you perform data analysis with R quickly and efficiently. The R language provides everything you need to do statistical work, but its structure ca......一起来看看 《R Cookbook》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具