React-HOOKS常用知识点总结.md
文章目录
1、类组件与函数组件的区别
类组件:
- 可以定义自己的state
- 保存自己的内部状态
- 有自己的生命周期,在对应的生命周期完成对应的逻辑操作,componentDidMount发送网络请求,数据更新时可以调用componentDidUpdate,状态发生改变会重新执行render函数
- 副作用需要在componentWillUmmount中清楚
- redux中需要手动调用subscribe
函数组件:
- 不能保存自己的状态,函数每次调用都是产生新的临时变量
- 函数组件被重新渲染时,整个函数都会被重新执行
2、HOOK使用原则
- 只能在最外层调用HOOK
- 不能在循环、条件判断或子函数中调用
- 如果需要做条件判断,则需要放到HOOK的内部
- 只能在React的函数组件调用HOOK
3、useState
作用:用于保存状态
参数:初始state,
返回值:一个数组【当前的函数值,设置新值的函数】
调用setState会做两件事情:设置新的值,组件重新渲染,根据新的值返回DOM结构
setState可以传入对象 | 函数,实现结果同于setState({})
每次调用setState都是在做替换操作
// 声明一个叫 “count” 的 state 变量。
const [count, setCount] = useState(0);
// 声明一个数组
const [firends, setFirends] = useState(['Marry', 'Cherry']);
// 添加用户和修改用户年龄
import React, { useState } from 'react';
export default function ComplexHookState() {
const [friends, setFrineds] = useState(['Marry', 'Cherry']);
const [students, setStudents] = useState([
{ id: 110, name: 'Marry', age: 18 },
{ id: 111, name: 'Cherry', age: 30 },
{ id: 112, name: 'Juice', age: 25 },
]);
const incrementAgeWithIndex = index => {
const newStudents = [...students];
newStudents[index].age += 1;
setStudents(newStudents);
};
const addFriends = () => {
let newFriend = [...friends, 'Lois'];
setFrineds(newFriend);
};
// 传递函数,可以拿到旧的值做更多的自定义操作
const addFriends = () => {
setFrineds((preValue) => {
return [...preValue, 'Lois']
})
};
return (
<div>
<h2>好友列表:</h2>
<ul>
{friends.map((item, index) => {
return <li key={index}>{item}</li>;
})}
</ul>
<button onClick={addFriends}>添加朋友</button>
<h2>学生列表</h2>
<ul>
{students.map((item, index) => {
return (
<li key={item.id}>
<span>
名字: {item.name} 年龄: {item.age}
</span>
<button onClick={e => incrementAgeWithIndex(index)}>age+1</button>
</li>
);
})}
</ul>
</div>
);
}
4、useEffect
作用:类似生命周期函数,可用于网络请求、手动更新DOM、事件监听。
参数:回调函数、回调函数
函数组件中可以定义多个effect,将函数组件中同类逻辑放在同一个effect中
// 相当于 componentDidMount 和 componentDidUpdate:
useEffect(() => {
// 使用浏览器的 API 更新页面标题
document.title = `You clicked ${count} times`;
});
// 组件被渲染时执行useEffect函数,组件被销毁执行useEffect中的return
// 返回一个函数:effect的可选清除机制,每一个effect都可以返回一个清除函数。react会在卸载的时候执行清除操作。
import React, { useState, useEffect } from 'react';
export default function MultiEffectHookDemo() {
const [count, setCount] = useState(0);
const [isLogin, setIsLogin] = useState(true);
// 当count改变了执行当前effect
useEffect(() => {
console.log('修改DOM', count);
}, [count]);
useEffect(() => {
console.log('订阅事件');
return () => {
console.log('取消订阅事件');
};
}, []);
// 将isLogin作为effect依赖项,当isLogin发生改变则执行网络请求
useEffect(() => {
console.log('网络请求');
}, [isLogin]);
const setCounter = () => {
setCount(count + 1);
};
const setLogin = () => {
setIsLogin(!isLogin);
};
return (
<div>
<h2>MultiEffectHookDemo</h2>
<h2>{count}</h2>
<button onClick={setCounter}>+1</button>
<h2>{isLogin ? 'admin' : '未登录'}</h2>
<button onClick={e => setIsLogin(!isLogin)}>注册/登录</button>
<button onClick={setLogin}>登录/注销</button>
</div>
);
}
第二个参数:传入数组类型(性能优化),监听依赖变量改变后再执行回调函数
- 不传:每次都会更新
- 传入空数组:只会执行一次
- 传入依赖变量:在依赖变量发生改变执行
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
5、useContext
接收一个 context 对象(React.createContext
的返回值)并返回该 context 的当前值。
当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider>
的 value
prop 决定。
当组件上层最近的 <MyContext.Provider>
更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext
provider 的 context value
值
父组件
// 创建context对象
import React, { createContext } from 'react';
export const UserContext = createContext();
<UserContext.Provider value={{name:'Jack'}}>
<ContextHookDemo/>
</UserContext.Provider>
子组件
import React, { useContext } from 'react';
import {UserContext} from "../App"
通过user拿到父组件共享过来的数据(对应的就是父组件传递的context对象)
const user = useContext(UserContext)
6、useReducer
useState的一种替代方案,主要是在处理逻辑复杂用于拆分,或者需要依赖原来的state时使用
reducer共享的仅仅是操作函数,数据不会共享
// home.js
import React, { useReducer } from 'react';
import reducer from './reducer';
export default function Home() {
const [state, dispatch] = useReducer(reducer, { counter: 0 });
const addCount = () => {
dispatch({
type: 'increment',
});
};
return (
<div>
<h2>Home当前计数: {state.counter}</h2>
{/* <button onClick={e => dispatch({ type: 'increment' })}>+1</button> */}
<button onClick={addCount}>+1</button>
<button onClick={e => dispatch({ type: 'decrement' })}>-1</button>
</div>
);
}
reducer.js
export default function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, counter: state.counter + 1 };
case 'decrement':
return { ...state, counter: state.counter - 1 };
default:
return state;
}
}
7、userCallbcak
用途:性能优化。传入一个回调函数返回执行后的函数,在依赖不改变或者没有添加依赖的情况下,返回的永远是同一个函数
使用场景:将组件中的函数传递给子组件进行回调使用时,使用useCallback对函数进行处理,useCallback直接对值进行优化
8、useMemo
用途:性能优化,返回一个memoized值(可以是对象或函数等),在依赖不改变或者没有添加依赖的情况下,返回的永远是同一个值
useMemo传入一个回调函数,返回一个值作为usememo的返回值,可以传入依赖项,当依赖项发生改变后函数才会重新执行一次
场景: 在将一个组件中的函数, 传递给子元素进行回调使用时, 使用useCallback对函数进行处理.
const increment2 = useCallback(() => {
console.log("执行increment2函数");
setCount(count + 1);
}, [count]);
<Button title="btn2" increment={increment2}/>
const Button = memo((props) => {
console.log("Button重新渲染: " + props.title);
return <button onClick={props.increment}>Button +1</button>
});
9、useRef
返回一个ref对象,返回的ref对象在组件的整个生命周期保持不变。
作用:
- 获取DOM对象
- 保存不变的值
// useRef引用DOM
import React, { useRef } from 'react';
export default function RefHookDemo01() {
const titleRef = useRef();
const inputRef = useRef();
// 通过ref获取到对象的dom对象并修改值
const changeDOM = () => {
titleRef.current.innerHTML = 'Hello World';
inputRef.current.focus();
console.log(testRef.current);
};
return (
<div>
<h2 ref={titleRef}>RefHookDemo01</h2>
<input ref={inputRef} type='text' />
<button onClick={changeDOM}>修改DOM</button>
</div>
);
}
Refs 转发
父组件通过ref获取子组件中DOM元素
作用:获取子组件DOM
import { useRef, forwardRef } from 'react';
const Input = forwardRef((props, ref) => {
return <input ref={ref} type='text' />;
});
function App() {
const inputRef = useRef();
const clearValue = () => {
inputRef.current.value = '';
};
return (
<div>
<Input ref={inputRef} />
<button onClick={clearValue}>清除</button>
<button onClick={e => inputRef.current.focus()}>聚焦</button>
</div>
);
}
export default App;
10、useImperativeHandle
useImperativeHandle对forwardRef做限制,限制子组件暴露过多内容给父组件
推荐:在使用forwardRef获取子组件时一起使用useImperativeHandle
childInputRef和fatherInputRef命名不影响,可以相同也可以不同
import { useRef, forwardRef, useImperativeHandle } from ‘react’;
const Input = forwardRef((props, ref) => {
const childInputRef = useRef();
useImperativeHandle(
ref,
() => ({
//父组件需要其他操作则暴露对应的函数
focus: () => {
childInputRef.current.focus();
},
}),
[childInputRef]
);
return <input ref={childInputRef} type=‘text’ />;
});
function App() {
const fatherInputRef = useRef();
return (
<div>
import { useRef, forwardRef, useImperativeHandle } from 'react';
const Input = forwardRef((props, ref) => {
const childInputRef = useRef();
useImperativeHandle(
ref,
() => ({
//父组件需要其他操作则暴露对应的函数
focus: () => {
childInputRef.current.focus();
},
}),
[childInputRef]
);
return <input ref={childInputRef} type='text' />;
});
function App() {
const fatherInputRef = useRef();
return (
<div>
<Input ref={fatherInputRef} />
<button onClick={e => fatherInputRef.current.focus()}>聚焦</button>
</div>
);
}
export default App;
11、useLayoutEffect
useEffect:渲染DOM更新后执行,不会阻塞DOM的更新
useLayoutEffect:渲染的内容更新到DOM上之前更新,会阻塞DOM更新
12、自定义hook
本质上:一种函数代码逻辑的抽取。将相同的逻辑抽取到单独的函数中,自定义hook需要使用use开头
自定义hook实现:Context共享数据
// app.js
import React, { createContext } from 'react';
// 新建context对象并导出
export const UserContext = createContext();
export const TokenContext = createContext();
// 给对应的context上添加value
<UserContext.Provider value={{name: "w", age: 18}}>
<TokenContext.Provider value="fdafdafafa">
<CustomContextShareHook/>
</TokenContext.Provider>
</UserContext.Provider>
user-hook.js
// 拿到app.js中提供的值,并作为函数返回值导出
import { useContext } from "react";
import { UserContext, TokenContext } from "../App";
function useUserContext() {
const user = useContext(UserContext);
const token = useContext(TokenContext);
return [user, token];
}
export default useUserContext;
context.js
// 在需要使用的组建中导入自定义hook,拿到里面提供的值
import React, { useContext } from 'react';
import useUserContext from '../hooks/user-hook';
export default function CustomContextShareHook() {
const [user, token] = useUserContext();
console.log(user, token);
return (
<div>
<h2>CustomContextShareHook</h2>
</div>
)
}
获取当前滚动到的位置
// scroll-position-hook.js
import { useState, useEffect } from 'react';
function useScrollPosition() {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollPosition(window.scrollY);
}
document.addEventListener("scroll", handleScroll);
return () => {
document.removeEventListener("scroll", handleScroll)
}
}, []);
return scrollPosition;
}
export default useScrollPosition;
// scroll.js
import React from 'react';
import useScrollPosition from '../hooks/scroll-position-hook';
export default function CustomScrollPositionHook() {
const position = useScrollPosition();
return (
<div style={{ padding: '1000px 0' }}>
<h2 style={{ position: 'fixed', left: 0, top: 0 }}>CustomScrollPositionHook: {position}</h2>
</div>
);
}
// 本地存储
// 一般写法
import React, { memo, useState, useEffect } from 'react';
export default memo(function App() {
const [name, setName] = useState(() => {
const name = JSON.parse(window.localStorage.getItem('name'));
return name;
});
useEffect(() => {
window.localStorage.setItem('name', JSON.stringify(name));
}, [name]);
return (
<div>
<h2>{name}</h2>
<button onClick={e => setName('wingchiehpih')}>设置name</button>
</div>
);
});
// 自定义hook
import {useState, useEffect} from 'react';
function useLocalStorage(key) {
const [name, setName] = useState(() => {
const name = JSON.parse(window.localStorage.getItem(key));
return name;
});
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(name));
}, [name]);
return [name, setName];
}
export default useLocalStorage;
// localstorage.js
import React from 'react';
import useLocalStorage from '../hooks/local-store-hook';
export default function CustomDataStoreHook() {
const [name, setName] = useLocalStorage('name');
return (
<div>
<h2>CustomDataStoreHook: {name}</h2>
<button onClick={e => setName('kobe')}>设置name</button>
</div>
);
}
});
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(name));
}, [name]);
return [name, setName];
}
export default useLocalStorage;
// localstorage.js
import React from ‘react’;
import useLocalStorage from ‘…/hooks/local-store-hook’;
export default function CustomDataStoreHook() {
const [name, setName] = useLocalStorage(‘name’);
return (
CustomDataStoreHook: {name}
<button onClick={e => setName(‘kobe’)}>设置name
);
}