react-hooks相关知识点
- useState 相关知识点
- useEffect 如何保证首次加载组件时执行,后续加载组件不执行回调函数
- useEffect 如何实现组件数据更新时的调用
- useEffect 如何实现组件卸载时的清理工作
- 如果 useEffect 内部使用了外部的函数或者数据 state ,但是没有在依赖项里做出设置,会出现什么情况。如何解决
- 说一下 useContext 的作用,和工作方式
- Hooks 中的组件渲染优化相关
- useMemo 的作用
- useRef 的作用
- useReducer 的作用
- 是否使用过 useReducer 搭配 useContext 来自己封装一个类似于 Redux 的公共状态管理的工具。
- useImperative 的作用
- react 路由中都提供了哪些 hooks 的用法。各自的作用是什么。如何使用。
- redux 中提供了哪些 hooks 的用法。各自的作用是什么。如何使用。
useState 相关知识点
useState 返回的更新数据函数被调用后,不会立刻拿到最新的结果
const DemoState = (props) => {
/* number 为此时 state 读取值 ,setNumber 为派发更新的函数 */
let [number, setNumber] = useState(0) /* 0为初始值 */
return (<div>
<span>{ number }</span>
<button onClick={ ()=> {
setNumber(number + 1);
console.log(number); /* 这里的 number 是不能够及时改变的 */
} } ></button>
</div>)
}
在函数组件中依次执行上下文中,state的值是固定不变的
function Index(){
const [ number, setNumber ] = React.useState(0)
const handleClick = () => setInterval(()=>{
// 此时 number 一直都是 0
setNumber(number + 1 )
},1000)
return <button onClick={ handleClick } > 点击 { number }</button>
}
function Index(){
const [ number, setNumber ] = React.useState(0)
const handleClick = () => setInterval(()=>{
// 此时 number 一直都是 0
setNumber(number + 1 )
},1000)
return <button onClick={ handleClick } > 点击 { number }</button>
}
当使用 useState 返回的函数修改引用数据类型时,如何保证页面渲染?
由于React官方在设计Hook时候,规定使用useState创建的数据,修改时,不像React类组件中那天,去合并原来的数据,而是直接完全替换原数据。
- 使用展开运算符来合并更新对象
- JSON.parse(JSON.stringify(‘引用类型变量’ )) 进行数据的深拷贝
当使用 useState 返回的函数修改基本数据类型时,如果值相同,页面是否会重新渲染?
- 不会重新渲染,但是如果是事件中修改了值,前后不一样, 事件执行两次以上会执行render两次 。
如何获取最新修改后的数据。
- 作为参数传递出去
- 使用 useEffect() 监听 state 变化
- 自定义 hook(回调函数)
useEffect 如何保证首次加载组件时执行,后续加载组件不执行回调函数
- 添加空依赖项
useEffect 如何实现组件数据更新时的调用
- 将需要更新的数据添加到依赖项中
useEffect 如何实现组件卸载时的清理工作
- 添加 return 方法,并添加空依赖项
如果 useEffect 内部使用了外部的函数或者数据 state ,但是没有在依赖项里做出设置,会出现什么情况。如何解决
- 尽可能将函数声明放在 useEffect 的内部,这样就不会构成闭包,从而解决控制台的警告,此时依赖项是无需操作的。
- 如果一些公共的函数确实需要在其他的函数中使用,比如公共的请求,公共的工具性函数,此时可以将函数声明继续放在 useEffect 的内部,同时使用 useRef 的 current 属性保存这个函数,那么其它函数中就可以使用 current 来获取这个公共的内容进行调用。
- 如果公共函数确实需要放在 useEffect 内部,那此时在 useEffect 内部就会形成闭包环境,控制台会发出警告,让去设置依赖项,但是不能设置依赖项,可能会造成外部函数死循环调用,可以使用 useCallback 对公共函数进行缓存,将缓存后的函数放在依赖项中,从而解决警告,又可以避免外部函数形成死循环调用
说一下 useContext 的作用,和工作方式
- useContext 可以帮助我们跨越组件层级直接传递变量,实现共享
- 第一步父组件使用 React.createContext() 创建 Context 对象:
const myContext = React.createContext(null);
- 使用 Context 对象.Provider 包裹子组件,使用 value 属性传值:
<myContext.Provider value={{ setNum, num }}> <Child></Child> </myContext.Provider>
- 子组件使用 useContext 获取传入的值:
const { setNum, num } = useContext(myContext);
Hooks 中的组件渲染优化相关
React.memo 的特性
- 它类似于类组件中的 PureComponent,可以对父组件传递过来的 prop 数据是否发生改变进行浅层比较,如果 prop 发生了改变就渲染子组件,prop 没有发生改变,子组件就不会渲染。
- 它只对比 prop,不会对当前组件的 state 进行对比
useCallback 的特性
- 它会对函数进行缓存,返回缓存后的函数。
- 当父组件的函数需要传递给子组件进行子组件向父组件传值时,由于传递的是普通函数,当父组件渲染时,会重新生成一个新函数,此时子组件的 React.memo 就不起作用了,需要使用 useCallback 将缓存后的函数传递过去,React.memo 才能发挥作用。
- 当组件内的某个函数必须要作为依赖项时,需使用 useCallback 进行缓存。
useMemo 的作用
- 它的第一个参数是函数,这个函数的返回值就是 useMemo() 要缓存的结果,一旦结果被缓存,后续组件中有连续使用这个结果的时候,直接读取缓存的值,不会重新计算。
useRef 的作用
- 用于操作 DOM。
- 充当全局的公共变量,保存任意数据至 current 中。且 useRef 每次返回的都是上一次修改的最新数据,不管组件如何渲染,current 里面的数据都不会重新初始化。
useReducer 的作用
- 以redux工作流的形式,来维护当前组件的数据声明及修改功能。是useState的替代方案。
是否使用过 useReducer 搭配 useContext 来自己封装一个类似于 Redux 的公共状态管理的工具。
- 使用 createContext 创建 context,其中初始化了 state的类型 和 actions的种类
export const initState = {
color: "blue"
};
export const ColorContext = createContext({
state: initState,
actions: {
updateColor: (color: string): void => {}
}
});
- 创建 reducer 去处理不同类型的 action
export enum ColorActionTypes {
UPDATE_COLOR = "UPDATE_COLOR"
}
export const reducer = (state, action) => {
switch (action.type) {
case ColorActionTypes.UPDATE_COLOR:
return {
...state,
color: action.color
};
default:
return state;
}
};
- 构建一个高阶的Provider组件,使用 useReducer 去构建初始状态并获取到 dispatch 函数,使用 dispatch 函数去发送不同的 action 到 reducer 对状态进行自动处理
import { FC, useReducer } from "react";
import { ColorContext, reducer, initState, ColorActionTypes } from "./utils";
const ColorProvider: FC = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initState);
const updateColor = (color: string) =>
dispatch({
type: ColorActionTypes.UPDATE_COLOR,
color
});
const actions = {
updateColor
};
return (
<ColorContext.Provider value={{ state, actions }}>
{children}
</ColorContext.Provider>
);
};
export default ColorProvider;
- 自定义一个hook,里面使用 useContext 获取到 state 和 actions,根据场景抛出某些 state 和 某些 actions
export const useColorContext = () => {
const { state, actions } = useContext(ColorContext);
const updateColor = (color) => actions.updateColor(color);
return {
color: state.color,
updateColor
};
};
- 在项目主入口文件使用高阶的Provider组件进行包裹,另外在需要使用 state 和 actions 的地方调用自定义的 hook 即可
export default function App() {
return (
<div className="App">
<ColorProvider>
<Text />
<Button />
</ColorProvider>
</div>
);
}
Button 组件导入 useColorContext 使用 updateColor action去更新状态
import React from "react";
import { useColorContext } from "./utils";
const Button = (props) => {
const { updateColor } = useColorContext();
return (
<div>
<button
style={{ color: "red" }}
onClick={() => {
updateColor("red");
}}
>
Red
</button>
<button
style={{ color: "blue" }}
onClick={() => {
updateColor("blue");
}}
>
Blue
</button>
</div>
);
};
export default Button;
Text 组件导入 useColorContext, 使用 color 状态
import React from "react";
import { useColorContext } from "./utils";
const Text = (props) => {
const { color } = useColorContext();
return <div style={{ color: color }}>字体颜色为:{color}</div>;
};
export default Text;
useImperative 的作用
- 搭配 forwardRef,可以实现子组件向父组件暴漏方法和属性,让父组件可以直接使用子组件的属性和方法。
react 路由中都提供了哪些 hooks 的用法。各自的作用是什么。如何使用。
- useNavigate:可以重定向
- useLocation:获取 location 对象
- useSearchParams:设置 url 中的查询参数
- useParams:获取动态路由参数值
redux 中提供了哪些 hooks 的用法。各自的作用是什么。如何使用。
- useSelector():从 Redux 存储状态中提取数据
- useDispatch():从 Redux 存储中返回对函数的引用,使用它来调度操作