React Hook的了解

一、 React Hook 的 简介:

Hook 是React 16.8的新增特性, 它可以让你在不编写class 的情况下使用statet 以及其他的React特性,Hook 使你在无需修改组件结构的情况下复用状态逻辑; 它将组件中相互关联的部分拆分成更小的函数(比如设置订阅或者请求数据),而非强制按照生命周期划分。

二、Hook解决的问题:

  • (1)、函数式组件,比class 更容易理解;
  • (2)、无需修改组件结构 复用状态逻辑,UI 和 逻辑更容易分离;
  • (3)、将组件相互关联的部分拆分成更小的函数,无生命周期;
  • (4)、避免地狱式嵌套,可读性提高;
  • (5)、解决HOC 和 Render Props 的缺点

 

三、 useState

state 的初始值为 { count : 0 }, 当用户点击按钮之后,我们通过调用this.setState() 来增加 state.count。

函数组件

(Hook 在 class 内部是不起作用的)

import React, { useState } from 'react'; 
     function Test() { 
    cosnt [count, setCount] = useStaet(0); 
    return (
     <div> 
         <p>You clicked {count} times</p> 
         <button onClick={() => setCount(count + 1)}>Click me </button> 
     </div> 
    ); 

}

useState 需要哪些参数? useState() 方法里面唯一的参数就是初始 state。不同于 class 的是,我们可以按照需要使用数字或字符串对其进行赋值,而不一定是对象。在示例中,只需使用数字来记录用户点击次数,所以我们传了 0 作为变量的初始 state。(如果我们想要在 state 中存储两个不同的变量,只需调用 useState() 两次即可。)

 

四、 useEffect

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

import React, { useState, useEffect } from 'react'; function Test() { const [count, setCount] = useState(0); useEffect( () => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You cliked {count} times</p> <button onClick={() => setCount(count + 1)}>Click me </button> </div> ); }

useEffect Hook 看做成componentDidMount、componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

effect 内部执行是异步的

 

默认情况下,它在第一次渲染之后和 每次更新之后都会执行。 effect 发生在“渲染之后”,React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

 

副作用操作中分为: 需要清除的 和 不需要清除的

(1)、无需清除的 effect

有时候,我们只想在 React 更新 DOM 之后运行一些额外的代码。比如发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。

(2)、需要清除的 effect

例如: 订阅外部数据源, 防止引起内存泄露

react 官网demo -- 使用 Class 的示例

通常会在componeDidMount 中设置订阅,并在componentWillUnmount 中清除它。例如,假设我们有一个ChatAPI 模块, 它允许我们订阅好友的在线状态:

class FriendStatus extends React.Component { 
    constructor(props) { 
      super(props); 
      this.state = { isOnline : null }; 
      this.handleStatusChange = this.handleStatusChange.bind(this); 
    } 
    componentDidMount() { 
        ChatAPI.subscribeToFriendStatus() { 
            this.porps.friend.id, this.handleStatusChange 
        } 
    } 
    componentWillUnmount() { 
        ChatAPI.subscribeToFriendStatus() { 
            this.props.friend.id, this.handleStatsuChange 
        } 
    } 
    handleStatusChange(status) { 
         this.setState({ isOnline: status.isOnline }); 
    }
    render() { 
        if (this.state.isOnline === null) { 
            return 'Loading...'; 
        }
         return this.state.isOline ? 'Online' : 'Offline'; 
    } }

 

使用 Hook 的示例:

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

function FriendStatus(props) {
    const [isOnline, setIsOnline] = useState(null);
    
    useEffect(() => {
        funcion handleStatusChange(status) {
            setIsOnline(status.isOnline);
        }
       ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
       return function cleanup() {
           ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
       }
    });
    if (isOnline === null) {
        return 'Loading...';
    }
    return isOnline ? 'Online' : 'Offline';
}

 

1)、为什么要在effect 中返回一个函数?

这是effect 可选的清除机制,每个effect 都可以返回一个清除函数; 如此可以将添加和移除订阅的逻辑放在一起,它们都属于effect 的一部分。

2)、React 何时清除effect?

React会在组件卸载的时候执行清除操作, effect 在每次渲染的时候都会执行; 这就是为什么React 会在执行当前effect 之前 对上一个effect 进行清除。

 

五、 useContext

const value = useContext(MyContext);

接收一个context对象,(React.createContext 的返回值)并返回改context 的当前值。 当前的context 值由上层组件中距离当前组件最近的 <MyContext.Provieder> 的value prop 决定。

当组件上层最近的<MyContext.Provider> 更新时, 该Hook 会触发重渲染,并使用 最新传递给MyContext provider 的context value 值。

useContext(MyContext)

const themes = {
    light: {
        foreground: "#000",
        background: "#eee"
    },
    dark: {
        foreground: "#fff",
        background: "#222"
    }
};

const ThemeContext = React.createContext(themes.light);
function App() {
    return (
       <ThemeContext.Provider value={themes.dark}>
           <Toolbar />
        </ThemeContext.Provider>     
    );
}
function Toolbar(props) {
    return (
      <div>
          <ThemedButton />
      </div>  
    );
}

function ThemedButton() {
    const theme = useContext(ThemeContext);
    return (
       <button style={{ background: theme.background, color: theme.foreground }}>
           I am styled by theme context!
       </button>     
    );
}

 

六、 useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer 接收类型(state, action)=> newState 的reducer,和redux工作方式一样, 并返回与分派方式配对的当前状态。

 

reducer 就是一个只能通过action 将 sate 从一个过程转换成一个过程的纯函数;

使用场景:

  • (1)、state 逻辑较复杂且包含多个子值,可以集中处理;
  • (2)、下一个state 依赖于之前的state;
  • (3)、想深层级修改子组件的一些状态,使用useReducer 还能给那些会触发更新的组件做性能优化;

使用reducer 有助于将读取和写入分开。

下面是 useState 部分的反例,重写为使用reducer:

const initialState = { count:0 };
function reducer(state, action) {
    switch (action.type) {
        case: 'reset':
            return initialState;
        case 'increment':
            return { count: state.count + 1 };
        case 'decrement':
            return { count: sate.count - 1 };
    }
}

function Counter({initialCount}) {
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <>
           Count: { state.count }
           <button onClick={() => dispatch({type: 'reset'})}>Reset</button>
           <button onClick={() => dispatch({ type: 'increment' })}>+</button>
           <button onClick={() => dispatch({ type: 'decrement'})}>-</button> 
        </>    
    );
}

 

七、 useRef

const refContainer = useRef(initialValue);

 

useRef 返回一个可变ref 对象,其.curretn 属性初始化为传递的参数 (initialValue);

返回的对象将在组将的整个生命周期内保持不变。

=》

useRef 可以保存任何可变的值,其类似于在class 中 使用实例字段的方式。

 

用法:

(1)、useRef 可以存储那些不需要引起页面重新渲染的数据;

(2)、如果你刻意地想要从某些异步回调中读取/最新的/state, 你可以用 一个ref来保存或修改它,并从中读取。

function TextButton() {
    const inputEl = useRef(null);
    const onButtonClick = () => {
        inputEl.current.focus();
    };
    return (
        <>
           <input ref={inputEl} type="text" />
           <button onClick={onButtonClick}>Focus the input</button> 
        </>
    )
}

 

八、 useDemo

const memoizedValue = useMemo(() => computeExpensiveValue(a,b), [a,b]);

 

传递一个“create” 函数 和 一个输入数组,useDemo 只会在其中一个输入发生更改时重新计算memoized。这种优化有助于避免对每个渲染进行昂贵的计算。

 

如果未提供数组,则每当将新函数实例作为第一个参数传递时,都将计算新值。(在每个渲染上使用内联函数)

import React, { useState, useMemo } from “react”;

 function Demo() {
     const [count, setCount] = useState(0);
     const handle = () => {
         return count;
     };
     
     const handle1 = useDemo(() => {
        console.log('handle1', count);
        return count; 
     }, []);
     
    const handle2 = useDemo(() => {
        console.log('handle2', count);   //大计算量的方法
        return count;
    }, [count]);
    console.log("render-top");
     
    return (
       <div>
           <p>
              demo: {count}
              <button onClick={() => setCount(count + 1)}>++count</button> 
           </p>
           <p>-------------------</p>
          <Child handle={handle1} />
        </div> 
    );      
 }

function Child({ handle }) {
  console.log("render-child");
  return (
    <div>
      <p>child</p>
      <p>props-data: {handle}</p>
    </div>
  );
}
export default Demo;

 

 

总结:

(1)、如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值;

(2)、useMemo 用于返回 memoize, 防止每次render 的时候,大计算量带来的开销。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值