React Hook 未来很美好

本篇文章参考以下博文

前言

  最近 React 项目上碰到一个内存泄漏问题,定位了好久,发现是 React 框架存在缺陷,销毁的节点并没有被真的销毁,导致越积越多,造成崩溃现象。在找修改方法的时候,由于出现问题的版本是 16.8 所以就以这个版本号为关键字进行搜索,结果出来的很多都是 React Hooks ,简单看了一下这个新特性,相比于原来的使用方式,直观感受更轻量级,更符合函数式编程的思想,而不是类编程。决定好好研究一下这个 API。预估这会代替现在的编程方式,进而成为主流。
在这里插入图片描述

一、函数组件

  React 的核心思想是组件化,尽可能的把任何一个模块都写成组件,并且通过类实现。这样的构建思想对于新手来说不太友好,因为和以前我们熟悉的那种结构来说,差别很大,需要一点时间去适应。

  其实组件最佳的写法,应该是函数,而不是类,比如 React 原生就支持以下写法。

function sayHello(props) {
	return <p>Hello World! My name is {props.name}</p>;
}

  虽然上面的写法很符合我们希望的样子,但是里面没有状态,也没有生命周期钩子函数,使用很局限,基本上只能渲染静态数据。

  所以 React Hooks 的设计初衷就是可以 完全替代类的函数组件

二、Hook 钩子

   所有钩子的都是为函数引入外部功能,React 默认提供一些常用钩子,同时也支持自己封装钩子。按照惯例,钩子的命名格式是以 use 开头,后面接功能名称。下面是默认提供的四个钩子。

useState()

useContext()

useReducer()

useEffect()

三、useState():状态记录

  该钩子替代了原来的state,可以提供给我们用来进行状态管理

import React, { useState } from "react";

export default function  ChangeContent()  {
  const  [text, setText] =  useState("Bfore");

  function handleClick()  {
    return setText("After");
  }

  return  <button  onClick={handleClick}>{text}</button>;
}

  上面这种书写格式,重新让我们回归到了,函数式编程的模式当中,一开始先定义个变量,中间是执行方法,最后返回结果。

  useState() 函数入参是状态的初始值,上例的初始值为按钮的文字。该函数返回一个数组,数组的第一个成员是一个变量(上例是 text),指向状态的当前值。第二个成员是一个函数,用来更新状态,约定是 set 前缀加上状态的变量名(上例是 setText)。

3.1 useState() 原理

  该钩子函数会返回当前状态的属性和设置状态的方法,而且当状态改变以后,会更新一遍视图,调用 render() 方法。

let memoizedState;	
function useState (initialState) {	
  memoizedState = memoizedState || initialState	
  function setState (newState) {	
    memoizedState = newState	
    render()	
  }	
  return [memoizedState, setState]	
}

3.2 多个 state 原理

上面代码在使用一个状态的时候,执行没有问题,但是当需要使用多个状态的时候,只能改变一个状态。例如如下情况。

import React, { useState } from "react";

export default function  ChangeContent()  {
  const  [text1, setText1] =  useState("button1");
  const  [text2, setText2] =  useState("button2");
  
  function handleClick1()  {
    return setText1("change1");
  }
  function handleClick2()  {
    return setText2("change2");
  }

 return 
    <div>
        <button onClick={handleClick1}>{text1}</button>
        <button onClick={handleClick2}>{text2}</button>
    </div>;
}

对于上面这种多个 state 的情况,需要使用数组来保存 state


let memoizedStates = []	
let index = 0	
function useState (initialState) {	
  memoizedStates[index] = memoizedStates[index] || initialState	
  let currentIndex = index	
  function setState (newState) {	
    memoizedStates[currentIndex] = newState	
    render()	
  }	
  return [memoizedStates[index++], setState]	
}

四、useContext():状态共享

  原来使用类组件的时候,状态共享,需要我们使用 redux,或者通过父子传值来完成,现在在 Hooks 中,给我们提供了这样一个钩子方法。

const AppContext = React.createContext({});
<AppContext.Provider value={{
  data: 'hello world'
}}>
  <div className="hooks">
    <Title/>
    <Content/>
  </div>
</AppContext.Provider>

  下面是 Title 组件内部

const Title = () => {
  const { data } = useContext(AppContext);
  return (
    <div className="child">
      <p>{data}</p>
    </div>
  );
}

  上面方法中,通过 useContext() 钩子函数引入 Context 对象,获取其中的 data 属性。

五、useReducer():状态管理

  熟悉 redux 的同学一定对 Reducer 不陌生,在 redux 中,更新状态需要 触发 action ,然后使用 Reducer 函数计算出新的状态,所以 Reducer 的函数形式是 (state, action) => newState

  hooks 中通过 useReducer() 来实现 Reducer 的功能。

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

  接收 Reducer 和初始 state 作为入参,返回状态当前值和 dispatch 函数。计数器示例如下:

const myReducer = (state, action) => {
  switch(action.type)  {
    case('countUp'):
      return  {
        ...state,
        count: state.count + 1
      }
    default:
      return  state;
  }
}

  组件内部代码。

function App() {
  const [state, dispatch] = useReducer(myReducer, { count:   0 });
  return  (
    <div className="App">
      <button onClick={() => dispatch({ type: 'countUp' })}>
        +1
      </button>
      <p>Count: {state.count}</p>
    </div>
  );
}

  当只需要共享状态和 Reducer 函数的时候,可以代替 redux 。但是当需要 中间件(middleware)和 时间旅行(time travel)的时候,还是需要使用 redux,配置 redux-thunkredux-devtools

六、useEffect():副作用钩子

  useEffect() 用来引入具有副作用的操作,最常见的就是向服务器请求数据。以前,放在 componentDidMount 里面的代码,现在可以放在 useEffect() ,用法如下。

useEffect(()  =>  {
  // Async Action
}, [dependencies])

  上面用法中,第一部分的函数中,用来放异步代码。第二部分的数组,是前面函数的依赖项,只有数组中的数据变化时,才会执行 useEffect() 。如果省略第二个参数,那只要组件渲染,就会执行 useEffect()

const Person = ({ personId }) => {
    const [loading, setLoading] = useState(true);
    const [person, setPerson] = useState({});

    useEffect(() => {
        setLoading(true);
        fetch('http://127.0.0.235/Loginfo', {
            method: 'POST',
            body: personId,
            headers: {
                'Content-Type': 'application/json',
                'Authorization': window['token']
            }
        }).then((response) => {
            if (response.data.ErrCode === 0) {
                setPerson(response.data);
                setLoading(false);
            }
        })
    }, [personId])

    if (loading === true) {
        return <p>Loading ...</p>
    }

    return (
    	<div>
	        <p>Name: {person.name}</p>
	        <p>Height: {person.height}</p>
	        <p>Weight: {person.mass}</p>
	    </div>
  );
}

七、封装自定义 Hooks

  上面的方法封装一下,就可以成为我们自定义的 Hook

const usePerson = (personId) => {
   const [loading, setLoading] = useState(true);
    const [person, setPerson] = useState({});
    useEffect(() => {
        setLoading(true);
        fetch('http://127.0.0.235/Loginfo', {
            method: 'POST',
            body: personId,
            headers: {
                'Content-Type': 'application/json',
                'Authorization': window['token']
            }
        }).then((response) => {
            if (response.data.ErrCode === 0) {
                setPerson(response.data);
                setLoading(false);
            }
        })
    }, [personId]);
    return [loading, person];
};

  使用自定义 Hook 方法如下:

const Person = ({ personId }) => {
  const [loading, person] = usePerson(personId);

  if (loading === true) {
    return <p>Loading ...</p>;
  }

  return (
    <div>
        <p>Name: {person.name}</p>
        <p>Height: {person.height}</p>
        <p>Weight: {person.mass}</p>
    </div>
  );
};

  

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值