React Hooks

官网:https://zh-hans.reactjs.org/docs/hooks-state.html#gatsby-focus-wrapper

Hook 是什么? 

  Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

  Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook。稍后我们将学习其他 Hook。

什么时候我会用 Hook? 

  如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其转化为 class。现在你可以在现有的函数组件中使用 Hook。

使用Hook的限制:

  • 只能写在函数组件和自定义Hook
  • 不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们

 

一、useState

  1.声明

  以往在class中声明一个变量count

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  使用useState声明一个count

import React, { useState } from 'react';

function Example() {
    // 声明一个叫 “count” 的 state 变量
    const [count, setCount] = useState(0);

  const [count, setCount] = useState(0);

  count:自定义state变量名,这里等同于this.state.count

  setCount:自定义函数名,等同于this.setSate

  useState只有一个参数,即为count的初始值,这里意为count初始值为0

  2.读取

  class

<p>You clicked {this.state.count} times</p>

  函数

<p>You clicked {count} times</p>

  3.更新

  class

  <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

  函数

  <button onClick={() => setCount(count + 1)}>
    Click me
  </button>

 

二、useEffect

  Effect Hook 可以让你在函数组件中执行副作用操作

  如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdate 和 componentWillUnmount 这三个函数的组合。

  1.使用

import React, { useState, useEffect } from 'react';

function Example() {
    const [count, setCount] = useState(0);
    // 异步,第一次渲染和每次更新完毕后执行 Effect 内操作
    useEffect(() => {
        console.log(`You clicked ${count} times`)
    });

    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </div>
    );
}

  2.清除

  React 何时清除 effect? React 会在组件卸载的时候执行清除操作。

  effect 在每次渲染的时候都会执行,所以 React 在执行当前 effect 之前对上一个 effect 进行清除。

  effect 如果返回一个函数,React 将会在执行清除操作时调用它

    useEffect(() => {
        console.log(`You clicked ${count} times`)
        return function(){
            console.log('清除 effect')
        }
    });

  3.跳过 Effect 进行性能优化

    useEffect(() => {
        console.log(`You clicked ${count} times`)
        return function(){
            console.log('清除 effect')
        }
    }, [count]); // 仅在 count 更改时更新

  如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect
  如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([]

  如果你传入了一个空数组([]),effect 内部的 props 和 state 就会一直拥有其初始值

 

三、useContext

  Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性

import React, { createContext, useContext } from 'react';

const themes = {
    light: {
        foreground: "#000000",
        background: "#eeeeee"
    },
    dark: {
        foreground: "#ffffff",
        background: "#222222"
    }
};
// 通过createContext在组件外创建 Context 对象,createContext(defaultValue)
// 使用 Context 对象的组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值
// 只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效
const ThemeContext = createContext(themes.light);

function App() {
    return (
        // ThemeContext.Provider提供了一个Context对象,这个对象是可以被子组件共享的。
        <ThemeContext.Provider value={themes.dark}>
            {/* Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据 */}
            {/* 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染 */}
            <Toolbar />
        </ThemeContext.Provider>
    );
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
    return (
        <div>
            <ThemedButton />
        </div>
    );
}

function ThemedButton() {
    // useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值
    // useContext 的参数必须是 context 对象本身
    const theme = useContext(ThemeContext);
    // 当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定
    return (
        <button style={{ background: theme.background, color: theme.foreground }}>
            I am styled by theme context!
        </button>
    );
}

 

 四、useReducer(可以先熟悉 Redux )

// 指定初始state
const [state, dispatch] = useReducer(reducer, {count: initialCount});
const [参数名,dispatch]= useReducer(reducer, 初始值);
// 惰性初始化
const [state, dispatch] = useReducer(reducer, initialArg, init);
const [参数名,dispatch]= useReducer(reducer, 初始函数传入值, init初始函数);

  useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

// 计数器示例
import React, { useReducer } from 'react';

// init函数
function init(initialCount) {
    return { count: initialCount };
}

// reducer
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: state.count - 1 };
        case 'reset':
            return init(action.payload);
        default:
            throw new Error();
    }
}

function Counter() {
    // useReducer(reducer, 传入init的初始值, init)
    const [state, dispatch] = useReducer(reducer, 0, init);
    return (
        <>
            Count: {state.count}
            <button
                onClick={() => dispatch({ type: 'reset', payload: 0 })}>
                Reset
        </button>
            <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
            <button onClick={() => dispatch({ type: 'increment' })}>+</button>
        </>
    );
}

 

五、useRef

  useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

// 示例:输入框聚焦
import React, { useRef } from 'react';

function TextInputWithFocusButton() {
    const inputEl = useRef(null);
    const onButtonClick = () => {
        // `current` 指向已挂载到 DOM 上的文本输入元素
        inputEl.current.focus();
    };
    return (
        <>
            <input ref={inputEl} type="text" />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    );
}

  useRef() 和自建一个 {current: ...} 对象的唯一区别是,useRef 会在每次渲染时返回同一个 ref 对象

  当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现。

 

六、自定义Hook

  自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook

  共享逻辑,代码封装

  自定义useReducer示例:

import React, { useState } from 'react';

// 自定义 useReducer ,命名用 "use"
function useReducer(reducer, initialState) {
    const [state, setState] = useState(initialState);

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

    // 返回state 和 dispatch
    return [state, dispatch];
}

// reducer 规则
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return state + 1;
        case 'decrement':
            return state - 1;
        default:
            throw new Error();
    }
}

function App() {
    // 使用自定义 Hook
    const [state, dispatch] = useReducer(reducer, 0);

    function handleIncrement() {
        dispatch({ type: 'increment' });
    }

    function handleDecrement() {
        dispatch({ type: 'decrement' });
    }
    return (
        <div>
            {state}
            <button onClick={() => handleIncrement()}>按钮+</button>
            <button onClick={() => handleDecrement()}>按钮-</button>
        </div>
    )
}

 

posted @ 2021-03-25 15:32  惊蛰丶  阅读(123)  评论(0编辑  收藏  举报