Time Bomb Veela
  1. 1 Time Bomb Veela
  2. 2 かかってこいよ NakamuraEmi
  3. 3 Libertus Chen-U
  4. 4 Quiet Storm Lyn
  5. 5 One Last You Jen Bird
  6. 6 Hypocrite Nush
  7. 7 Warcry mpi
  8. 8 Last Surprise Lyn
  9. 9 Life Will Change Lyn
  10. 10 The Night We Stood Lyn
  11. 11 Flower Of Life 发热巫女
2018-11-05 22:30:18

由React Hooks所想到的

继Fiber,Suspence之后,最近React又提出了一版突破原有理论的API——Hooks,真是难以想象这都是一个版本之内做出的改变。React真的是太强了,学不动了。。

期望解决的问题

当然调侃归调侃,还是要紧跟时代的步伐。彻底的变化必然是能够解决相当大的痛点的。那么Hooks的目的是什么呢?本质上就是五个字——复用和维护。先不谈这个API的具体细节,我们先想想现在的React在复用和维护上有什么问题。

当使用无论什么框架时,我们大概都会经历这样一个过程——先是一个简单的组件,随着业务升级,组件越来越复杂,那么首先想到的就是拆分为可复用的小组件。当完成拆分后就会有数据通信的问题,于是有了各种各样的状态管理工具来解决这个问题,将组件间通信大致分为两种模式——通过redux之类的全局状态通信和通过props向下传递状态的通信,同时在拆分过程中,为了更好的复用一些逻辑,又创造了HOC和render props的组件组织形式。

那么最大的问题就来了——为了解决JS层面复用的问题,我们不得不在UI层面创建大量的无关UI的wrapper组件去单独处理JS复用,这样不仅带来了props通信困难的问题,同时使得组件树变得越来越臃肿,调试和维护的成本也越来越高。函数式编程提倡的data到view的映射中间,不得不生硬的增加一层view去处理data,而这一层本来是更适合用JS去做的事,为何不直接就在data到view这一层通过JS处理掉呢?

Hooks所做的就是这样一件事,函数式组件不再是单纯的data到view的映射关系,而在其中追加上了一层“映射所依赖的逻辑和状态”,类似于函数式编程中的Monad,这样一下子就解决了状态管理和逻辑复用的难点。

  • 没有依赖的状态放置到各自的组件Hooks中单独管理,和全局状态管理解耦的同时增加组件的内聚,同时这层Hooks可以和view的渲染拆开,更方便复用。
  • 组件的逻辑复用层面也将从HOC或者render props中抽离到各自组件的Hooks,打平组件的层次,将这些JS层面的东西从组件树中剥离,成为一个真正的独立可复用的逻辑单元。

那么下面就该看看Hooks究竟是如何使用的了。

使用

首先要知道它的本意就是在纯函数中hook一些依赖。必须遵守如下两条规则。

  • 只能在函数式组件中调用Hooks
  • 只能在函数顶层调用,不能出现在循环或者条件判断中。

大致可以分为如下几类。

State Hook

用于在函数式组件中使用state。这个Hook将原先每个组件的唯一state拆开,并将state中的每个值和能改变其值并重渲染的方法关联。官方用例如下:

import { useState } from 'react';
function Example() {
  // Declare a new state variable, which we'll call "count"
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

可以看到这样的写法相当直观清爽,一眼就能看到state相关联的变量和方法,同时这部分声明state的逻辑也更容易从这个函数式组件中拆出以实现复用。

Effect Hook

从class组件迁移到function组件需要解决的另一个问题就是,class组件从创建到销毁所经过的生命周期。生命周期的引入无非就是为了在某个特定阶段做一些sideEffects,那么为什么不把这个阶段统一成一个API呢?实际上React本身就这么做了,从getDerivedStateFromProps的提出到Effect Hook,React有意的弱化生命周期的概念,将重点从"组件生命周期本身"迁移到"数据的来源与获取"上来,这样当然是更纯粹的,React的核心本身也就是数据和渲染,所有适合在生命周期做的事转移到数据到渲染过程中的sideEffects。官方实例如下:

import { useState, useEffect } from 'react';
function Example() {
  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>
  );
}

其他

其实以上两类Hooks已经能大致了解这个新API了,除此之外还有更多的Hooks也可以查阅官方文档,这里就不赘述。使用上还是很简单的,关键还是要理解其在"复用和维护"上传达出来的新的思想,可以看一则官方拆分实例感受一下。

import React from 'react';
import { Card, Row, Input, Text } from './components';
import ThemeContext from './ThemeContext';

export default class Greetings extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            name: 'Mary',
            surname: 'Popins',
            width: width.innerWidth
        };
        this.handleNameChange = this.handleNameChange.bind(this);
        this.handleSurnameChange = this.handleSurnameChange.bind(this);
        this.handleResize = this.handleResize.bind(this);
    }
    componentDidMount() {
        window.addEventListener('resize', this.handleResize);
        document.title = this.state.name + '' + this.state.surname;
    }
    componentDidUpdate() {
        document.title = this.state.name + '' + this.state.surname
    }
    componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
    }
    handleNameChange(event) {
        this.setState({name: event.target.value});
    }
    handleSurnameChange(event) {
        this.setState({surname: event.target.value});
    }
    handleResize() {
        this.setState({width: innerWidth});
    }
    render() {
        const { name, surname, width } = this.state;
        return (
            <ThemeContext.Consumer>
                {theme => (
                    <Card theme={theme}>
                        <Row label="Name">
                            <Input value={name} onChange={this.handleNameChange}/>
                        </Row>
                        <Row label="Surname">
                            <Input value={surname} onChange={this.handleSurnameChange}/>
                        </Row>
                        <Row label="Width">
                            <Text>{width}</Text>
                        </Row>
                    </Card>
                )}
            </ThemeContext.Consumer>
        )
    }
}
import React, { useState, useContext, useEffect } from 'react';
import { Card, Row, Input, Text } from './components';
import ThemeContext from './ThemeContext';

function useFormInput(initialValue) {
    const [value, setValue] = useState(initialValue);
    function handleChange(e) {
        setValue(e.target.value);
    }
    return {
        value,
        onChange: handleChange
    }
}

function useDocumentTitle(title) {
    useEffect(() => {
        document.title = title;
    });
}

function useWindowWidth() {
    const [width, setWidth] = useState(window.innerWidth);
    useEffect(() => {
        const handlewResize = () => setWidth(window.innerWidth);
        window.addEventListener('resize', handleResize);
        return () => {
            window.removeEventListener('resize', handleResize);
        };
    });
    return width;
}
export default function Greetings(props) {
    const name = useFormInput('Mary');
    const surname = useFormInput('Poppins');
    const theme = useContext(ThemeContext);
    const width = useWindowWidth();

    useDocumentTitle(name.value + '' + surname.value);
    return (
        <Card theme={theme}>
            <Row label="Name">
                <Input {...name}/>
            </Row>
            <Row label="Surname">
                <Input {...surname}/>
            </Row>
            <Row label="Width">
                <Text>{width}</Text>
            </Row>
        </Card>
    )
}

小结

可以看出现在React中函数式组件可以表达的东西越来越多,函数式编程真的是可以重点学习的一块了,毕竟data到view的映射本质就很适合用函数式的思想去写,再搭配上hooks引入的monad思想,rxjs等函数式编程思想的状态管理框架,组件不就可以用state和sideEffect完美表达了吗?随着Fiber的落地,未来的前端开发真的就是只有想不到没有做不到了。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「React」 标签。