https://github.com/daotin/react-in-one-day
一天学会react
https://github.com/daotin/react-in-one-day
Last synced: 6 months ago
JSON representation
一天学会react
- Host: GitHub
- URL: https://github.com/daotin/react-in-one-day
- Owner: Daotin
- Created: 2024-11-18T01:28:29.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-11-20T07:31:40.000Z (over 1 year ago)
- Last Synced: 2025-01-22T18:32:46.674Z (over 1 year ago)
- Language: HTML
- Size: 21.5 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# react-in-one-day
## 一天学会 react
快速学习路径:
1、jsx 语法、useState 和 useEffect 了解下,基础用法和 Vue 对比着学: [https://component-party.dev/](https://component-party.dev/) 。
2、然后敲个 todolist ,期间有什么不会的直接问 gpt
搞完这些就可以说自己会 React 了。
react 文档:https://zh-hans.react.dev/learn
---
> 下面的react状态管理库如何选择?Redux、Dva、React-Toolkit、MobX,zustand,以及 useContext 结合 useReducer 的管理方式
Redux系列:Redux Toolkit > Redux > Dva
推荐尽量选择现代化、社区活跃的库(如 Redux Toolkit 或 Zustand)
最终,react技术栈选择:Vite + React + Ant Design + Zustand + TailwindCSS
react 项目模板:
- slash-admin(1.8k): https://github.com/d3george/slash-admin (需要的依赖好多)
- react-ts-template:https://github.com/huangmingfu/react-ts-template (依赖多,没有ant design,因为考虑到移动端,对应的就是antd mobile了)
- react-app-template:https://github.com/Gzbox/react-app-template (没有Zustand)
---
## React 的不可变数据设计
React 的不可变数据设计是一种处理数据状态的哲学和模式,强调**不要直接修改原始数据,而是通过创建数据的副本来更新状态**。这种设计理念与 React 的高效更新机制密切相关,确保组件能够正确地重新渲染。
- 对于引用数据类型,数据一旦创建,不能直接修改;需要通过生成新数据的方式来更新状态。
- 对于基本数据类型,它们本身就不会被直接修改,而是被重新赋值。因此,对于基本数据类型,不需要像对象或数组那样进行拷贝操作,直接赋值即可。
### 为什么 React 使用不可变数据设计?
1. 提高性能
React 使用 虚拟 DOM 来高效更新 UI:
- 每次状态更新,React 通过比较前后状态的引用来判断是否需要更新组件。
- 如果状态的引用未改变,React 会跳过重新渲染,提高性能。
- 使用不可变数据设计能确保每次更新时生成一个新的引用,从而触发正确的更新。
2. 方便调试
- 不可变数据设计使得状态变更变得更加明确。
- 你可以在状态变更前后轻松比较数据,快速定位问题。
- 例如,调试工具(如 Redux DevTools)依赖这种机制来记录和回溯状态。
3. 数据安全性
- 避免直接修改原始数据可能带来的副作用(如意外更改其他地方引用同一对象的数据)。
- 确保状态变更在预期范围内。
## useEffect
`useEffect` 是 React 中的一个 Hook,用于在函数组件中执行副作用操作。副作用操作包括数据获取、订阅、手动更改 DOM 等。`useEffect` 接受两个参数:一个是副作用函数,另一个是依赖项数组。
### 基本用法
```javascript
import React, { useState, useEffect } from 'react';
function MyComponent({ someProp }) {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('副作用函数执行');
// 设置一个定时器
const interval = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
// 返回清理函数
return () => {
console.log('清理函数执行');
clearInterval(interval); // 清理定时器
};
}, [someProp]); // 依赖项数组
return
Count: {count};
}
export default MyComponent;
```
- 如果`依赖项数组`为空,那么只会在`组件加载`的时候,执行`副作用函数`,然后在组件卸载的时候执行`清理函数`。
- 如果`依赖项数组`不为空(比如为 someProp),那么当 someProp 发生变化时,`清理函数`会先执行,然后`副作用函数`会重新执行。最后当组件卸载时,`清理函数`又会执行。
## 学习井字棋游戏的 2 点困惑
1、React 的状态更新是异步的,即便是使用 setTimeout 获取的仍是旧的数据。在 vue 中可以使用 nextTick,在 react 中一般的的做法是什么?(不想使用 useEffct)
2、在 vue 中,所有的状态都可以写在对顶层,然后全局可用,但是 react 中,所有的状态,函数都是要由顶层组件传递过去,非常麻烦。比如 Game 需要传递很多便利和函数给 Borad,是我代码设计不合理吗?
在 React 中,如果你想在状态更新后立即获取最新值,有以下几种方法:
- 使用 flushSync
```js
import { flushSync } from 'react-dom';
function handleClickSquare(i) {
flushSync(() => {
setSquareArray(newSquareArray);
});
// 这里可以立即获取到更新后的值
console.log(squareArray);
}
```
- 使用状态更新的回调形式
```js
function handleClickSquare(i) {
setSquareArray(newSquareArray, () => {
// 在这里可以访问到最新的状态
if (newWinner) {
alert(`${newWinner} is winner!`);
}
});
}
```
- 使用 useRef 跟踪最新值
```js
function Game() {
const [squareArray, setSquareArray] = React.useState([]);
const squareArrayRef = React.useRef(squareArray);
// 当 squareArray 更新时,更新 ref
React.useEffect(() => {
squareArrayRef.current = squareArray;
}, [squareArray]);
// 需要获取最新值时,使用 squareArrayRef.current
}
```
2. 关于状态管理
确实,React 的 props 传递可能会显得比较繁琐。对于这个问题,有以下几种解决方案:
- 使用 Context API
```js
// 创建 Context
const GameContext = React.createContext();
function Game() {
const [squareArray, setSquareArray] = React.useState([]);
const [nextStep, setNextStep] = React.useState('X');
// ... 其他状态
const contextValue = {
squareArray,
setSquareArray,
nextStep,
setNextStep,
// ... 其他状态和方法
};
return (
Next step: {nextStep}
);
}
// 在子组件中使用 Context
function Board() {
const { squareArray, setSquareArray, nextStep } = React.useContext(GameContext);
// ... 组件逻辑
}
```
- 使用状态管理库
对于较大的应用,可以使用 Redux、Zustand 或 Jotai 等状态管理库
这些库提供了更强大的全局状态管理能力
- 重构组件结构
```js
// 将相关逻辑封装到自定义 Hook
function useGame() {
const [squareArray, setSquareArray] = React.useState([]);
const [nextStep, setNextStep] = React.useState('X');
const handleMove = React.useCallback((i) => {
// 处理移动逻辑
}, []);
return {
squareArray,
nextStep,
handleMove,
};
}
// 在组件中使用
function Game() {
const gameLogic = useGame();
return (
);
}
```
## doubleCount 自动是响应式的吗?
```js
function Demo() {
const [count, setCount] = useState(10);
const doubleCount = count * 2; // 每次 count 改变时,这行代码都会重新执行
// ...
}
```
是的,doubleCount 是自动响应式的!
在 React 中,每当组件的状态(比如 count)发生变化时,组件会重新渲染,这时所有在组件函数体内的代码都会重新执行。
### 在 react 组件中什么时候组件重新渲染?
在 React 中,组件重新渲染的情况主要有以下几种:
### 1. 状态(State)变化
```javascript
function Component() {
const [count, setCount] = useState(0);
// 调用 setCount 会触发重新渲染
const handleClick = () => setCount(count + 1);
}
```
### 2. 属性(Props)变化
```javascript
function Child(props) {
// 当父组件传入的 props 变化时,Child 组件会重新渲染
return
{props.value};
}
function Parent() {
const [value, setValue] = useState(0);
// value 改变会导致 Child 重新渲染
return ;
}
```
### 3. 父组件重新渲染
```javascript
function Parent() {
const [count, setCount] = useState(0);
return (
{/* Parent 重新渲染时,Child 也会重新渲染 */}
);
}
```
### 4. Context 值变化
```javascript
const MyContext = React.createContext();
function Child() {
const value = useContext(MyContext);
// Context 值变化时,使用该 Context 的组件会重新渲染
return
{value};
}
```
### 优化方案:
1. **使用 memo 阻止不必要的重新渲染**
```javascript
const Child = React.memo(function Child(props) {
// 只有当 props 真正变化时才重新渲染
return
{props.value};
});
```
2. **使用 useMemo 缓存计算值**
```javascript
function Component() {
const expensiveValue = useMemo(
() => {
// 复杂计算
return heavyComputation();
},
[
/* 依赖项 */
]
);
}
```
3. **使用 useCallback 缓存函数**
```javascript
function Parent() {
const handleClick = useCallback(
() => {
// 处理点击
},
[
/* 依赖项 */
]
);
return ;
}
```
4. **合理拆分组件和状态**
```javascript
function Parent() {
return (
{/* 静态组件不需要重新渲染 */}
{/* 只有需要更新的部分重新渲染 */}
);
}
```