banner
NEWS LETTER

React踩坑指南

Scroll down

如何写css:在tsxjsx文件中引入.scss/less/css文件
元素的事件绑定:在元素添加属性onXxx,例如<button onClick={handler}>,其中传递的必须是函数,不能是语句调用,如果要定义内联函数,需要将其包装在匿名函数中,比如:
`<button onClick={() => alert(“xx”)}>

Hooks

【重要】Hook需要在非条件语句中使用,调用 Hook 时,例如 useState,仅在组件或另一个 Hook 的顶层被调用才有效。

设置一次state将请求一次重新渲染。
state的状态存储在组件之外

每一次渲染都有一次独属于这次渲染的state快照,同样,不同次渲染内的事件也是各自不同的,在过去某一个渲染中的事件处理函数使用的是那一次渲染所拥有的state快照

只有同一个位置的同一个组件state才会被保留下来,当从UI树中移除一个组件时,React会销毁它的state(要注意的是此处的位置指的是组件在UI树中的位置,非在jsx代码中的位置)。如果一个组件在UI树中的位置没有变化,但是需要重置它的state,可以为它设置key属性以赋予它一个明确的身份,常用场景:重置表单的state

react内置hooks

useState

useActionState(原为useFormState)可以根据某个表单动作的结果更新state

1
const [state, formAction, isPending] = useActionState(fn, initialState, permalink)

useContext

useRef

useEffect

性能Hook

useMemo可以缓存计算结果
useCallback 可以缓存函数,在多次渲染中,只要依赖项不变都将返回同样的函数。它的应用场景有:

  • 跳过子组件的重新渲染。父组件的重新渲染会递归地渲染所有的子组件,首先将子组件用memo包裹,当props未发生变化时,子组件会跳过重新渲染,再配合useCallback,使作为props的函数保持不变,就能跳过重新渲染该子组件

useMemouseCallback常常一同出现,用来做性能优化。useMemo用来缓存

有用的hooks

useReducer 作为useReducer第一个参数的reducer函数命名取自于数组的reduce方法,接收 当前状态 和 action处理方法,返回下一个状态

1
2
3
4
5
6
import { useReducer } from 'react';
// useReducer接受两个参数,第一个为reducer函数,第二个是初始值
// 返回两个值,1为有状态的值,2是dispatch函数,用来派发操作到reducer
const [tasks, dispatch] = useReducer(reducer, initialData);
// 一个reducer函数通常会用switch来处理各种状态逻辑,接受两个参数,1 state ,2 action对象。返回更新后的state
// reducer应该是纯净的函数,

immer可以简化reducer

ref的使用

当你希望组件记住某些信息,但又不要触发新的渲染,可以使用useRefuseRef会返回一个对象{ current: [initial_data] },例如:

1
2
3
import { useRef } from 'react';
const ref = useRef(0);
console.log(ref.current)

使用useRef和使用普通变量的区别:
首先要明确一点:每次触发新的渲染,react组件中的代码都会重新执行一遍。对普通变量x而言,渲染引发的代码重新执行都会产生一个新的x变量引用,而使用useRef定义的变量会返回保留下来的状态值

使用useRefuseState的区别:

  • useRef声明的变量在改变时不会触发重新渲染组件,而使用useState声明的变量在setState时会重新渲染组件。
  • ref 在渲染过程之外可以任意修改和更新,如ref.current = ref.current + 1;而state的修改只能通过setState函数,从而触发重新渲染(在某一次具体的快照中state是不可修改的,只能被替换为另一个值)

ref的使用场景通常是:当你的组件需要“跳出” react 并与外部 API 通信。例如:

  • 用于存储timeout id;
  • 存储和操作dom元素;(常用)
  • 存储不需要被用来计算jsx的其他对象;
    这些都是不会用于dom渲染展示的场景

Effect的使用

什么是Effect
大写E开头的Effectreact中定义的一个词,用来描述 由渲染引起的副作用
Effect通常用于暂时跳出react与一些外部系统进行同步,比如 网络请求,浏览器API。
Effect的执行时机是 提交结束后/页面更新结束后 这两个

创建一个 Effect
其步骤为:

  1. 声明一个Effect
  2. 指定Effect依赖项
  3. 必要时添加清理操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import {useEffect} from 'react';
    function component() {
    // useEffect的第二个参数是依赖项数组
    // 不传表示每次渲染后运行
    // 传入空数组表示只在组件首次挂载时运行
    // 传入非空数组则在组件首次挂载 以及 依赖项发生变化时运行
    useEffect(() => {
    //
    }, [])
    }

理解Effect

  • Effect的部分特性可类比到vue中的mounted等生命周期钩子函数,但Effect要更灵活
  • 与vue不同,Effect的作用是为了保持组件与外部系统的同步,这种同步可能是由于某些依赖项(state和props这样的响应式数据)的变化,也可能是由于有必要这么做
  • 当编写一个Effect时,要考虑的有两点:1如何开始同步;2如何停止同步(通过返回一个清理函数)
  • 代码中的每个Effect应该代表一个独立的同步过程

useEffect 的依赖项是空数组时,回调函数只会执行一次,回调函数会保留初始状态的闭包,如果里面用到state数据,那么使用的会是初始状态的state而不是最新的,解决办法是使用函数式更新,比如;

1
2
3
4
5
6
const [data, setData] = useState([])
useEffect(() => {
// ...code
setData((prev) => [...prev, newProp])
// 而不是 setData([...data, newProp])
}, [])

React的重新渲染

问:如何触发react组件的渲染
答:react组件的初始渲染重新渲染,组件的生命周期分为:触发 - 渲染 - 提交dom,其中提交dom一环在应用真实dom操作之后,还会执行useEffectuseLayoutEffect的内容(useEffect 能获取到更新后绘制完成的真实dom内容,

1
2
3
4
5
sequenceDiagram
React->>Browser: 更新DOM(React work)
React->>React: 同步执行useLayoutEffect
Browser->>Browser: 计算布局和绘制
React->>React: 调度useEffect

什么时候会触发react组件的重新渲染?
答:当且只当state发生变化时,会触发react组件的重新渲染

用vite搭建一个简单的react项目

指令

1
npm create vite@latest my-react-app -- --template

模板生成的项目目录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
├── eslint.config.js
├── index.html
├── package.json
├── public
│   └── vite.svg
├── README.md
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   ├── index.css
│   └── main.jsx
└── vite.config.js

vite.config.js中用到了@vitejs/plugin-react插件

其他文章