React18学习笔记
React18学习笔记
笔记参考资料:
https://www.bilibili.com/video/BV13h4y177jW (主要参考)
https://www.bilibili.com/video/BV1bS4y1b7NV
一、React中的基础概念
1.1 React 和 JSX
首先 JSX 并不是 React 特有的东西,但大多数React项目为了方便都会使用JSX,JSX的基本规则如下
- 定义虚拟DOM时不要写引号
- 标签中混入JS表达式要使用花括号
- 样式的类名不能使用class,要使用className
- 内联样式,要用 style={{key:value}}的形式去写key要求是小驼峰
- 只有一个根标签
- 标签必须闭合
- 标签首字母
标签首字母小写,则将该标签转为对应的HTML元素,若不存在,则报错
标签首字母大写,React 去渲染对应的组件,若组件未定义,则报错
1.2 React中的样式
1.2.1 行间样式
<div style={{width:100,height:100}}></div>
1.2.2 局部样式
局部样式命名 xxx.module.css
如果使用vite构建,可以在 vite.config.js中配置如下内容,以支持 css module 小驼峰写法
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import eslintPlugin from "vite-plugin-eslint";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
eslintPlugin({
include: ["src/*.jsx", "src/*.js", "src/**/*.jsx", "src/**/*.js"],
}),
],
css:{
// 这是主要的配置
modules:{
localsConvention: 'camelCaseOnly'
}
}
});
使用
<div className={style.headerTitle}>
index
<h1 className="header-title">title</h1>
</div>
1.2.3 全局样式
写在 App.css 或者 index.css 中的样式,会有透传的效果,这里就不测试了
1.3 sass 和 classnames 模块的使用
sass作为css预处理器
classnames解决的问题是如何在jsx中更灵活的使用 className,我是这么理解的。
yarn add sass sass-loader
yarn add classnames
示例
import classNames from "classnames";
function App() {
const myClass = classNames({
box1: true,
box2: false,
});
return <div className={myClass}>jsx</div>;
}
1.3 jsx条件控制
1.3.1 通过 if...else的形式
export default function index() {
const flag = true;
let content = "";
if (flag) {
content = <p>我是一个p标签</p>;
}else{
content = <p>我是另一个p标签</p>;
}
return <div>{content}</div>;
}
1.3.2 通过 三元表达式
export default function index() {
const flag = true;
return <div>{flag ? <p>我是一个p标签</p> : <p>我是另一个p标签</p>}</div>;
}
1.3.3 通过 && 或者 ||
export default function index() {
const flag = true;
return <div>{flag && <p>我显示出来了</p>}</div>;
}
1.4 jsx 遍历渲染
通过数组map方法
export default function MyHeader() {
const list = [
{ id: 1, name: "A" },
{ id: 2, name: "B" },
{ id: 3, name: "C" },
]
return (
<div>
{
list.map((item) => {
return div keyitemitemdiv
1.5 点标记写法
<React.StrictMode>
<App />
</React.StrictMode>,
1.6 组件间通信
关于如何灵活的使用 props
1.7 react 组件必须是纯函数
我觉得这个小节不应该作为一个单独的小结,有点大了,但还是讲一下
首先,什么是纯函数?
其次,React组件为什么要是纯函数?
1.8 组件状态
1.8.1 什么是组件状态
1.8.2 状态是如何改变视图的
1.8.3 多状态是如何进行正确记忆的
useState必须是按照顺序的,简单点说,就是不能写到条件语句里,这样会导致不确定性,如下代码会报错
// React Hook "useState" is called conditionally.
// React Hooks must be called in the exact same order in every component
import { useState } from "react";
export default function MyHeader() {
const flag = true
const [count, setCount] = useState(0);
if(flag){
const [name, setName] = useState("John");
}
const [age, setAge] = useState(18);
1.8.4 什么是状态快照及快照的陷阱
import { useState } from "react";
export default function MyHeader() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count);
}
return (
<div>
count:{count}
<button onClick={handleClickbutton
如上代码,console.log(count)初次点击打印的值应为0。
1.8.5 状态队列与自动批处理
export default function MyHeader() {
const [count, setCount] = useState(0);
const handleClick = () => {
// setCount(count + 1); -> setCount(c => count + 1)
// setCount(count + 1); -> setCount(c => count + 1)
// setCount(count + 1); -> setCount(c => count + 1)
setCount((c => c + 1)); // 0 + 1 = 1
setCount((c => c + 1)); // 1 + 1 = 2
setCount((c => c +
1.8.6 严格遵守状态是不可变的
xzxx
1.8.7 常见的对象和数组解决方案

1.8.8 immer简化不可变对象操作
immer是一个第三方库
npm install immer use-immer
1.8.9 惰性初始化状态的值
function computed(n){
return n + 1 + 2 + 3;
}
export default function MyHeader() {
const [count, setCount] = useState(() => computed(0));
// ...
}
1.8.10 状态提升来解决共享问题
就是父子通信
1.8.11 状态的重置处理问题

1.8.12 利用状态产生计算变量
1.9 受控组件和非受控组件
通过 props 控制的组件称为受控组件,通过state 控制的组件称为非受控组件
二、React Hooks
以use 开头的函数称为钩子。 usestate 是 React 提供的内置钩子。你可以在 API 手册 中找到其他内置的钩子。你也可以结合现有的钩子编写自己的钩子。
钩子比其他函数更具限制性。你只能在组件(或其他钩子)的顶部调用钩子。如果你想在条件或循环中使用usestate,请提取一个新组件并将其放在那里
2.1 useState

2.2 useRef
- 用 Ref 值做记忆功能。解决 定时器清空的问题
function App() {
const [count, setCount] = useState(0);
let timer = useRef(null);
const handleClick = () => {
setCount(count + 1);
clearInterval(timer.current);
timer.current = setInterval(() => {
console.log(1);
- useRef操作dom
由于 React 会自动处理更新 DOM 以匹配你的染输出,因你在组件中通常不需要操作 DOM。但是,有时你可能需要访问由 React 管理的 DOM 元素例如,让一个节点获得焦点、滚动到它或测量它的尺寸和位置
案例一:简单操作DOM
function App() {
const myRef = useRef(null);
const handleClick = () => {
myRef.current.style.backgroundColor = "red";
}
return (
<div>
<button onClick={handleClick}>点击改变div背景色</button>
<div ref={myRef}>
xxxxx
</div>
</div>
);
案例二:循环操作DOM
function App() {
const list = [
{id:1, name: "张三"},
{id:2, name: "李四"},
{id:3, name: "王五"},
]
return (
<div>
<ul>
{
list.map(item => {
return <li key=item ref
- 组件设置ref需要forwardRef进行转发
看一下如下的示例
const MyInput = forwardRef(function MyInput(props,ref) {
return <input type="text" ref={ref}/>;
});
function App() {
const myRef = useRef(null);
const handleClick = () => {
console.log(myRef.current);
myRef.current.focus();
myRef.current.value = "Hello, useRef"
- uselmperativeHandle 自定义ref的暴露
这个的作用就是说我可以自定义对外暴露Ref引用具体能干什么事
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => {
return {
focus(){
inputRef.current.focus();
},
focusAndStyle(){
inputRef.current.focus();
inputRef.current.
2.3 useEffect
2.3.1 纯函数如何处理副作用
什么是副作用?
useEffect的基本使用
初始渲染和更新渲染,都会触发useEffect()->因为每次渲染JSX后,后会触发useEffect(),当整个组件渲染完成后触发。
2.3.2 useEffect依赖项的使用
useEffect(() => {},[])
useEffect(() => {},[count])
2.3.3 尽量在 useEffect 内定义函数
不正确的写法
export default function App() {
const [count, setCount] = useState(0);
const [msg, setMsg] = useState("");
const fn = () => {
console.log("count", count);
};
useEffect(() => {
fn();
}, [fn])
上述写法是先定义了函数,然后在 useEffect 中使用该函数作为依赖项,这样很容易导致页面死循环,推荐或者说正确的写法应该是将函数声明和调用写在 useEffect 内,如下:
export default function App() {
const [count, setCount] = useState(0);
const [msg, setMsg] = useState("");
useEffect(() => {
const fn = () => {
console.log("count", count);
};
fn();
}, [])
2.3.4 useEffect清理操作的重要性
退出清理
如何使用异步操作、async await 如何使用
2.3.5 useEffectEvent(不常用)
暂定编写,18.2版本中没有,需要下载最新版
2.3.6 useLayoutEffect(不常用)
useEffect()是在渲染被绘制到屏幕之后执行的,是异步的; useLayoutEffect()是在染之后但在屏幕更新之前,是同步的
大部分情况下我们采用 useEffect() ,性能更好。但当你的 useEffect 里面的操作需要处理DOM,并会改变页面的样式,就需要用 useLayoutEffect ,否则可能会出现闪屏问题
2.3.7 useInsertionEffectDOM(不常用)
DOM更新前触发,这个通常可能会用于 css-in-js的库里
2.4 useReducer
2.4.1 Reducer统一的状态管理集合