一. 认识函数组件
class组件——功能全、东西多;麻烦
函数组件——功能弱、东西少;方便
函数组件:
1.状态——能变的、重新渲染
class √
fc × hook
2.this——当前实例
class √
fc ×
3.ref——引用
class √
fc × hook
4.方法
class √
fc × hook
hook:一套工具函数的集合——增强fc的功能
函数组件专用的
二. 认识hook
hook:一套工具函数的集合——增强fc的功能
hook!=函数组件
1.状态——useState的使用 参考链接
注意第四点,useState也可以传入一个函数,这个函数只会被调用一次,用在初始值是动态值的情况。
例如:
在上面代码中 const propsCount = props.count || 0 是不合理的,因为每次setState都会执行这行代码,而这行代码的意图是在初始化的时候执行,因此我们需要在useState的参数里放入一个函数。
改进写法:
对useState理解:
// App.jsx
<Content
// key={tabValue} 重置key与不重置key对useState的影响
tabValue={tabValue}
/>
// Content.jsx
import { getDealColumns, getUVColumns } from './config';
function Content(props) {
if (tabValue === 'Trade') {
getTableColumns = getDealColumns;
} else if (tabValue === 'UVIndex') {
getTableColumns = getUVColumns;
}
const [tableColumns, setTableColumns] = useState(getTableColumns());
console.log(tableColumns); // 切换tabs,如果没有重置key,这里的tableColumns不会变
}
使用useState初始的变量可以等价于为在class组件中的constructor里定义的state,如果组件没有重置key,那么useState初始的变量不会改变,重置了key,才会改变。
因此,状态尽量全部扔到Redux里。
useState是一个异步操作,会自动合并
即:在一个事件里触发多次setState,只会触发一次render
import React, {useState} from 'react';
export default function CounterHook() {
const [count, setCount] = useState(() => 10);
console.log("CounterHook渲染");
function handleBtnClick() {
// 下面这种只会加10
// setCount(count + 10);
// setCount(count + 10);
// setCount(count + 10);
// setCount(count + 10);
// 下面这种会加40
setCount((prevCount) => prevCount + 10);
setCount((prevCount) => prevCount + 10);
setCount((prevCount) => prevCount + 10);
setCount((prevCount) => prevCount + 10);
}
return (
<div>
<h2>当前计数: {count}</h2>
<button onClick={handleBtnClick}>+10</button>
</div>
)
}
通过import() 动态导入组件:
import React, { FC, useEffect, useState } from 'react';
import Test from './Test1';
console.log(11, Test)
type Props = {
name: string;
age: number;
};
const App: FC<Props> = props => {
const [count, setCount] = useState(0);
const [Comp, setComp] = useState(() => {
return () => {
return <div>11</div>
}
});
useEffect(() => {});
const handleClick = async() => {
setCount(count + 1);
const content = () => import('./Test1');
const { default: Comp } = await content();
// 对于import进来的组件,在setState的时候需要仍然是一个函数,因为前面useState类型的时候就定义了是一个函数,所以必须setState的时候是同样类型的
setComp(() => Comp)
};
return (
<div>
<h1>hello</h1>
<div>
{props.name}
{props.age}
</div>
<div>
<button onClick={() => handleClick()}>按钮{count}</button>
</div>
<Comp />
</div>
);
};
export default App;
useState: https://zh-hans.reactjs.org/docs/hooks-reference.html#usestate
2.影响——useEffect
把操作推迟到渲染完成,操作的结果会在下次渲染体现
模拟一部分生存周期函数
3.引用——useRef/forwardRef
问题:如何在父组件中引用子组件的某个元素?
这时候就用到了forwardRef
参考链接:forwardRef
4.上下文——useContext
theme主题
5.数据——useReducer
三. state
useState(初始值) => [值, function set];
this.state={
x: ?
}
this.state.a=99; //×
this.setState({ //√
a: 99
});
this.setState
react的状态——在渲染的过程中,保持不变的;所有的修改存着,等到渲染完成之后一块改、并且触发重新渲染
性能——理论上会降低(极少),极大提升程序稳定性
StrictMode——严格模式
1.找到所有的依赖项
2.渲染
只存在于development模式,打包以后就没了
四. hook使用注意事项
1. hook注意事项:
- hook只能用在函数组件中
- class组件不行
- 普通函数也不行
- 函数组件内部的函数也不行
2.hook跟顺序有关
- hook函数一定要放在函数组件的第一层——别放在if、for、while
五. hook 底层原理
hook过程
- 获取自身的值
- 如果有——不是第一次执行
直接返回已有的值 - 如果没有——是第一次执行
构建一个值
存起来
返回出去
伪代码如下:
阅读源码,不能逐行去抠,而是弄懂里面的原理;
hook工作原理 — 本质其实是reducer的原理(state+action => {…state})
hook1+action=>hook2
hook2+action=>hook3