hooks

React Hooks

对函数型组件进行增强
1、React Hooks 介绍
1.1 React Hooks 是用来做什么的
对函数型组件进行增强, 让函数型组件可以存储状态, 可以拥有处理副作用的能力.

让开发者在不使用类组件的情况下, 实现相同的功能.

1.2 类组件的不足 (Hooks 要解决的问题)
缺少逻辑复用机制
为了复用逻辑增加无实际渲染效果的组件,增加了组件层级 代码显示十分臃肿
增加了调试的难度以及运行效率的降低
类组件经常会变得很复杂难以维护
将一组相干的业务逻辑拆分到了多个生命周期函数中 (与Vue2.0相似,Vue3.0就增加了Composition API (组合式 API)来解决此问题)
在一个生命周期函数内存在多个不相干的业务逻辑
类成员方法不能保证this指向的正确性
经常要使用 bing 或者 嵌套函数来保证,使得代码看起来复杂;
2、 React Hooks 使用
Hooks 意为钩子, React Hooks 就是一堆钩子函数, React 通过这些钩子函数对函数型组件进行增强, 不同的钩子函数提供了不同的功能.

useState()
useEffects()
useReducer()
useRef()
useCallback()
useContext()
useMemo()

2.1 useState()

用于为函数组件引入状态

import React, { useState } from 'react';

function App () {
	const [count, setCount ] = useState(0);
	return <div>
		<span>{count}</span>
		<button onClick={() => setCount(count + 1)}> +1 </button>
	</div>;
}

使用说明:

接收唯一的参数即状态初始值. 初始值可以是任意数据类型.
返回值为数组. 数组中存储状态值和更改状态值的方法. 方法名称约定以set开头, 后面加上状态名称.
方法可以被调用多次. 用以保存不同状态值.
参数可以是一个函数, 函数返回什么, 初始状态就是什么, 函数只会被调用一次, 用在初始值是动态值的情况.
设置状态值方法的参数可以是一个值也可以是一个函数.
设置状态值方法的方法本身是异步的.

import React, { useState } from 'react';

function App () {
  const [ count, setCount] = useState (0)
  const [ person, setPerson] = useState ({ name: "张三", age: 20})
	function handlecount() {
	  setCount (count => {
	  	return count + 1
	  })
	}
	document.title= count
  return  <div>
	<span>{count} {person.name} {person.age}</span>   
	<button onClick={handlecount}>+ 1</button>
	<button οnclick={ () => setPerson({...person, name:"李四"}) }> setperson </button>
  </div>
}

2.2 useReducer()
useReducer是另一种让函数组件保存状态的方式.

import React, { useReducer } from  'react';


function App () {
    function reducer  (state, action) (
	  switch (action.type) {
	    case 'increment':
		  return state + 1
	  }
	}
	const [ count, dispatch ] = useReducer(reducer, 0)
	return  <div>
		<span>{ count }</span>
		<button onClick={ () => dispatch({ type: 'increment' })}> +1</button>
	</div>
}

2.3 useContext()
在跨组件层级获取数据时简化获取数据的代码.

import { createContext, useContext } from 'react';
const countcontext = createContext();
function App() {
  return (
    <countcontext.Provider value={100}>
      <Toolbar />
    </countcontext.Provider>
  );
}

function Toolbar() {
  const value = useContext(countcontext);  
  return <div>{value}</div>;
}

2.4 useEffect()
让函数型组件拥有处理副作用的能力. 类似生命周期函数.

useEffect 执行时机
可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合. 有三种情况

useEffect(() => {}) => componentDidMount, componentDidUpdate 组件挂载完成执行一次 组件数据更新完成之后执行
useEffect(() => {}, []) => componentDidMount 组件挂载完成执行一次
useEffect(() => () => {}) => componentWillUnMount 组件被卸载之前执行

useEffect 使用方法
为window对象添加滚动事件,组件卸载时移除
设置定时器让count数值每隔一秒增加1 ,组件卸载时清除定时器

useEffect 解决的问题
按照用途将代码进行分类 (将一组相干的业务逻辑归置到了同一个副作用函数中)
简化重复代码, 使组件内部代码更加清晰

import { useEffect } from "react";
import ReactDOM from 'react-dom';

function Api (props) {  

  function scroller() {
    console.log('滚动了')
  }
  useEffect(() => {
    window.addEventListener('scroll', scroller)
    return () => {
      window.removeEventListener('scroll', scroller)
    }
  }, [])
  return <div>
    <button onClick={ () => ReactDOM.unmountComponentAtNode(document.getElementById('root')) }>卸载组件</button>
  </div>
}

只有指定数据发生变化时触发effect

useEffect(() => {
  document.title = count
}, [count])

useEffect 结合异步函数
useEffect中的参数函数不能是异步函数, 因为useEffect函数要返回清理资源的函数, 如果是异步函数就变成了返回Promise

useEffect(() => {
  (async () => {// 自执行
      await axios.get()
  })()
})

2.5 useMemo()
useMemo 的行为类似Vue中的计算属性, 可以监测某个值的变化, 根据变化值计算新值.

useMemo 会缓存计算结果. 如果监测值没有发生变化, 即使组件重新渲染, 也不会重新计算. 此行为可以有助于避免在每个渲染上进行昂贵的计算.

import { useMemo } from "react";

const result = useMemo(() => {
  return count *2
}, [count])

2.6 memo 方法 (性能优化)
性能优化, 如果本组件中的数据没有发生变化, 阻止组件更新. 类似类组件中的 PureComponent 和 shouldComponentUpdate

会返回一个新的组件

import { memo } from "react";

const Counter = memo(function Count(){
  return <div>123</div>
})

export default Counter;

2.7 useCallback() 方法 (性能优化)
性能优化, 缓存函数, 使组件重新渲染时得到相同的函数实例. 防止组件因为函数实例不同而重新渲染

import { memo, useCallback, useState } from "react";

function Api () {
  const [count, setCount] = useState(0);
  const resetCount = useCallback( () => setCount(0), [setCount])
  return <div>
    <div>{count}</div>
    <button onClick={ () => setCount(count+1) }> +1</button>
    <Test resetCount={resetCount}/>
  </div>
}

const Test = memo(function Test1(props){
  console.log('组件渲染了')
  return <div>
    Test
    <button onClick={ props.resetCount }> reset </button>
  </div>
})  

2.8 useRef()
获取DOM元素对象

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

保存数据 (跨组件周期)
即使组件重新渲染, 保存的数据仍然还在. useFef保存的数据被更改不会触发组件重新渲染. 举例:可以用于清除定时器

function App() {
  let timer = useRef();
  useEffect(() => {
    timer.current = setInterval(() => {
      setCount(count + 1)
    },1000)
  })
  const stopCount = () => {
    clearInterval(timer.current)
  }
  return <div>{count}</div>
}
  1. 自定义 Hook
    自定义 Hook 是标准的封装和共享逻辑的方式.
    自定义 Hook 是一个函数, 其名称以 use 开头.
    自定义 Hook 其实就是逻辑和内置 Hook 的组合.
    主要目的就为了实现,组件之间的数据共享
import { useState, useEffect } from 'react';
// 自定义Hook
function useFriendStatus() {  
  const [list, setList] = useState({});
  useEffect(() => {
    axios.get('http://xxx').then(res => setList(res.data))
  }, []);
  return [list, setList];
}
function App() {  
  const [list, setList] = useFriendStatus();
  return <div> {list} </div>
}

// form hooks

import React, { useState } from 'react';
const useForm=function () {
    const [value, setValue]
    return {
        value:value,
        onChange:e=>setValue(e.target.value)
    }
}
function App() {  
    const personform= useForm()
    const pwdform= useForm()
    function submit(e) {
        e.preventDefault()
        console.log(personform.value);
        console.log(pwdform.value);
    }
    return <form onFinish={submit}>
        <input name='person' type='text' {...personform}/>
        <input name='pwd' type='password' {...pwdform}/>
        <button type='submit'>submit</button>
    </form>
  }

React 路由 Hooks
react-router-dom 路由提供的钩子函数

import { 
  useHistory, 
  useLocation,
  useRourteMatch,
  useParams
} from 'react-router-dom'
// 获取对应的路由对象

5.useState 钩子函数简单实现

import React from 'react';
import ReactDOM from 'react-dom';

let state = [];
let setters = [];
let stateIndex = 0;

function createSetter (index) {
  return function (newState) {
    state[index] = newState;
    render ();
  }
}

function useState (initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState;
  setters.push(createSetter(stateIndex));
  let value = state[stateIndex];
  let setter = setters[stateIndex];
  stateIndex++;
  return [value, setter];
}

function render () {
  stateIndex = 0;
  effectIndex = 0;
  ReactDOM.render(<App />, document.getElementById('root'));
}
function App() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('Tom');
  return <div>
    {count}
    <button onClick={ () => setCount(count + 1) }>+1</button>
    <button onClick={ () => setName('JDAK')}>名字</button>
  </div>;
}

6.useEffect 钩子函数简单实现

function render () {
  effectIndex = 0;
  ReactDOM.render(<App />, document.getElementById('root'));
}


let prevDepsAry = [];
let effectIndex = 0;

function useEffect(callback, depsAry) {
  // 判断callback是不是函数
  if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect函数的第一个参数必须是函数');
  // 判断depsAry有没有被传递
  if (typeof depsAry === 'undefined') {
    // 没有传递
    callback();
  } else {
    // 判断depsAry是不是数组
    if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect函数的第二个参数必须是数组');
    // 获取上一次的状态值
    let prevDeps = prevDepsAry[effectIndex];
    // 将当前的依赖值和上一次的依赖值做对比 如果有变化 调用callback
    let hasChanged = prevDeps ? depsAry.every((dep, index) => dep === prevDeps[index]) === false : true;
    // 判断值是否有变化
    if (hasChanged) {
      callback();
    }
    // 同步依赖值
    prevDepsAry[effectIndex] = depsAry;
    effectIndex++;
  }
  1. useReducer 钩子函数简单实现
7. function useReducer (reducer, initialState) {
   const [state, setState] = useState(initialState);
   function dispatch (action) {
  const newState = reducer(state, action);
  setState(newState);
   }
   return [state, dispatch];
 }

function App() {
function reducer (state, action) {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      return state;
  }
}
const [count, dispatch] = useReducer(reducer, 0);
return <div>
  {count}
  <button onClick={() => dispatch({type: 'increment'})}>+1</button>
  <button onClick={() => dispatch({type: 'decrement'})}>-1</button>
</div>;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小李科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值