反应钩
useState
useState是个反应自带的一个钩子函数,它的作用就是声明状态变量。useState这个函数接收的参数是我们的状态初始值(初始状态),它返回了一个数组,这个数组的第[0]项是当前的状态值,第[1]项是可以改变状态值的方法函数。
import React, { useState } from 'react';
function Example() {
const [age, setAge] = useState(18);
const [money, setMoney] = useState(1000);
return (
<div>
<button onClick={() => { setAge(age + 1) }}>{age}</button>
<button onClick={() => { setMoney(money + 1) }}>{money}</button>
</div>
)
}
export default Example;
useEffect
如果你熟悉React class的生命周期函数,你可以把useEffect Hook看做componentDidMount,componentDidUpdate和componentWillUnmount这三个函数的组合。使用useEffect,可以直接在函数组件内部处理生命周期事件。
useEffect使用需要注意的地方
- 有两个参数的回调和依赖性
- 如果依赖项不存在,那么回调每次渲染都会执行
- 如果依赖项存在,只有当它发生了变化,回调才会执行
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
function useEffectTest() {
useEffect(() => {
// 默认情况下,每次渲染后都会调用该函数
console.log('render!');
// 如果要实现 componentWillUnmount,
// 在末尾处返回一个函数
// React 在该函数组件卸载前调用该方法
// 其命名为 cleanup 是为了表明此函数的目的,
// 但其实也可以返回一个箭头函数或者给起一个别的名字。
return function cleanup () {
console.log('unmounting...');
}
})
return "useEffectTest";
}
停止重新重新渲染都会执行useEffect如果希望效果在运行,可以提供第二个参数-值表。将其视为该效果的依赖关系。如果其中一个依赖项自上次更改后,effect将再次运行。
初步有数据的副本:
const [value, setValue] = useState(0);
useEffect(() => {
// 仅在 value 更改时更新
console.log(value);
// 仅在组件卸载时更新
return () => {
console.log('组件卸载'+{ value })
}
}, [value])
初步空洞
const [value, setValue] = useState(0);
useEffect(() => {
// 仅在第一次渲染时更新
console.log(value);
// 仅在组件卸载时更新
return () => {
console.log('组件卸载'+{ value })
}
}, [])
useContext
useContext可以实现共享状态最大的改变是可以在使用Counter的时候不必在包裹Children了,比方说我们先创造了一个副本,这个主体里头有一个称为count的状态,以及一个修改count的方法setCount
在父组件中,使用createContext创建上下文并导出,然后通过值传递给子组件
import React, { createContext, useState } from 'react';
import { Counter } from './Counter'
// 创建一个上下文,这个上下文要导出
export const CountContext = createContext()
function UseContext() {
const [count, setCount] = useState(0)
return (
<div>
{/* 使用创建的上下文,数据通过 value 传递给子组件,这里我们传递了一个对象 */}
<CountContext.Provider value={{count,setCount}}>
<Counter />
</CountContext.Provider>
</div>
)
}
export default UseContext
在子组件中,引入父组件创建的一部分,然后使用useContext接收父组件通过value传递的值
import React, { useContext } from 'react';
// 这里获取父组件的上下文
import { CountContext } from './index'
export function Counter() {
// 通过 useContext 获取父组件传递的内容(d对象解构)
const { count, setCount } = useContext(CountContext)
return (
<div>
<h2>{count}</h2>
<button onClick={() => { setCount(count + 1) }}>count</button>
</div>
)
}
useReducer
Redux的核心概念是,组件发出action与状态管理器通信。状态管理器收到的action以后,使用Reducer函数算出新的状态
- 简单来说reducer是一个函数(状态,动作)=> newState:接收当前应用的状态和触发的动作action,计算并返回最新的状态。
useReducer的作用就是创造了一个状态管理器,它接收一个reducer和初始的state值。它返回了一个数组,这个数组的第[0]项是当前的state,第[1]项是可以改变state的方法函数dispatch,它会传递一个action给状态管理器。
const [状态,调度] = useReducer(reducer,initialState)
import React, { useReducer } from 'react';
// 创建的 reducer
function reducer(state, action) {
switch (action.type) {
case 'add':
return { count: state.count + 1 };
case 'del':
return { count: state.count - 1 };
default:
return state;
}
}
function UseReducer() {
const initialState = { count: 0 }
// 利用 reducer 和 初始state 创建一个状态管理器,返回 state 和 dispatch
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<p>{state.count}</p>
{/* 利用 dispatch 传递一个 action 给状态管理器 */}
<button onClick={()=>{dispatch({type:'add'})}}>add</button>
<button onClick={()=>{dispatch({type:'del'})}}>del</button>
</div>
)
}
export default UseReducer;
通过useReducer和useContext可以实现redux,useReducer创建状态管理器,useContext将管理器传递给所有的子类(通过useContext传递状态和调度)
useMemo
useMemo使用函数的形式来声明组件,失去了shouldCompnentUpdate(在组件更新之前)这个生命周期,从而我们没有办法通过组件更新前条件来决定组件是否存在更新。而且在函数组件中,不再也。区分mount状语从句:update两个状态,这意味着函数组件的每一次调用都会执行内部的所有逻辑,就带来了非常大的性能损耗。useMemo状语从句:useCallback都是解决上述性能问题的。
React.memo() 是判断一个函数组件的渲染是否重复执行;
useMemo() 是定义一段函数逻辑是否重复执行;
useMemo(() => fn, inputs)跟useCallback(fn, inputs)效果一样。
import React, { useMemo } from 'react';
function UseMemo() {
// useMemo 接收两个参数,一个是 callback,第二个参数是一个数组,与 useEffect 类似
const increase = useMemo(() => {
if (value > 2) return value + 1;
}, [value]);
}
export default UseMemo;
useRef
- 使用useRef获取React JSX中的DOM元素,获取后你就可以控制DOM的任何东西了。但是一般不建议这样来作,React接口的变化可以通过状态来控制。
- 用useRef来保存变量,渲染周期之间共享数据的存储(状态不能存储跨渲染周期的数据,因为状态的保存会触发组件重渲染),不过这个功能不多用,因为有了useContext。
获取DOM报表
import React, { useEffect, useRef } from 'react';
function UseRef() {
// 定义一个 ref
const h1Ref = useRef();
useEffect(() => {
// 输出获取到的DOM节点
console.log('useRef',h1Ref)
}, [])
return (
<div>
{/*保存 h1 的ref到 h1Ref */}
<h1 ref={h1Ref}>Hello World!</h1>
</div>
)
}
export default UseRef;