React基础
准备工作
需要装的Vscode插件
- Prettier Formatter for Visual Studio Code
- VS Code ES7+ React/Redux/React-Native/JS snippets
需要安装浏览器插件
- React Developer Tools
创建React应用
Create React APP 方式
# 安装 create-react-app 工具
yarn global add create-react-app
# 创建React项目
create-react-app app-name
Vite 方式
# 执行命令,创建React应用
npm create vite@4.1.0
React基础概念
关于tsx后缀
.ts后缀结尾的文件表示普通的TypeScript文件.tsx后缀结尾的文件表示组件.d.ts后缀结尾的文件是类型声明文件
关于JSX
-
React 程序使用JSX语法描述组件的dom结构
-
JSX: JavaScript XML
-
在下面网站中可以查看JSX语言是如何工作的
关于组件根节点
- 组件只允许有一个根节点元素,如果需要多个的话可以使用 Fragment 组件包裹
- 或者使用简写
关于useState方法
- 如果一个值需要再更新时dom也即时更新,需要使用useState方法声
- 在react中这称为钩子 hook
let [selectedIndex, setSelectedIndex ] = useState(-1);
// -1 为selectedIndex 的初始值
// setSelectedIndex 为更新这个值的方法
关于props
- 当需要从父组件向子组件传值的时候使用props
- 需要子组件通知父组件的时候,也可以通过props传入
- children 是特殊的props,它表示的是当前组件中的子节点(ReactNode)
props与state
不同点
- 父组件传入子组件中的props应该是只读的,不可更改的
- state中定义的值,是可以更新的。
相同点
- props中的值或者state中 的值更新后,react都会更新dom
关于快捷输入
- 在vscode中输入 rafce,可以快捷创建一个组件的基本架构,(需要安装 VS Code ES7+ React/Redux/React-Native/JS snippets)
关于Hook
useState
useState,它是用于在函数组件中使用状态的Hook,它返回一对值:当前状态和设置状态的函数。可以使用数组解构来将返回值赋值给变量名。
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useEffect
useEffect,它是用于在函数组件中使用生命周期的Hook,它接收两个参数,第一个参数是一个函数,称为“副作用函数”,用于定义副作用的操作,第二个参数是一个数组,称为“依赖项数组”,用于告诉React该副 作用函数依赖哪些数据,当这些数据发生变化时,React会重新调用这个副作用函数。
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
useContext
useContext,它是用于在函数组件中使用上下文的Hook,它接收一个上下文对象作为参数,返回该上下文对象的当前值。
import React, { useContext } from 'react';
import { UserContext } from './UserContext';
function Example() {
const user = useContext(UserContext);
return (
<div>
<p>{user.name}</p>
<p>{user.age}</p>
</div>
);
}
useReducer
useReducer,它是用于在函数组件中使用Reducer的Hook,Reducer是一种用于状态管理的设计模式。它接收两个参数,一个是Reducer函数,另一个是初始状态值。
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}
useCallback
useCallback,它是用于在函数组件中缓存函数的Hook,它接收一个函数和一个依赖项数组,当依赖项数组发生变化时,react会重新生成这个函数。
import React, { useState, useCallback } from 'react';
function Example () {
const [count, setCount] = useState(0)
const handleClick = useCallback(() => {
setCount(count + 1)
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
)
}
useMemo
useMemo,它是用于在函数组件中缓存数据的Hook,它接收一个函数和一个依赖项数组,当依赖项数组发生变化时,React会重新计算这个函数。
function Example () {
const [firstName, setFirstName] = useState('First Name')
const [lastName, setLastName] = useState('Last Name')
const fullName = useMemo(() => {
return firstName + ' ' + lastName
}, [firstName, lastName])
return (
<div>
<input type="text" value={firstName} onChange={e => setFirstName(e.target.value)} />
<input type="text" value={lastName} onChange={e => setLastName(e.target.value)} />
<p>{fullName}</p>
</div>
)
}
useRef
useRef,它是用于在函数组件中获取DOM节点或者缓存任何可变值的Hook,它返回一个包含最新DOM节点或最新值的对象。
function Example () {
const inputRef = useRef(null)
const handleClick = () => {
inputRef.current.focus()
}
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus Input</button>
</div>
)
}
关于Vue中的计算属性
在React中,可以使用Memo和useCallback Hook来实现类似Vue中计算属性的功能。
使用Memo Hook
Memo Hook可以将结果缓存起来,只有当依赖项发生变化时才会重新计算结果,类似于Vue中的计算属性。
示例:
import React, { useState, useMemo } from 'react';
function Example () {
const [count, setCount] = useState(0)
const doubleCount = useMemo(() => {
console.log('Double Count Calculated')
return count * 2
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<p>Double Count: {doubleCount}</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
在上面的例子中,doubleCount是一个计算属性,只有当count变化时,doubleCount才会被重新计算。
使用useCallback Hook
useCallback Hook可以缓存函数,防止多次渲染时重复创建函数,类似于Vue中的计算属性。
示例:
import React, { useState, useCallback } from 'react';
function Example () {
const [count, setCount] = useState(0)
const handleClick = useCallback(() => {
console.log('Button Clicked')
setCount(count + 1)
}, [count])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
)
}
在上面的例子中,handleClick是一个计算属性,只有在count变化时,才会重新创建该函数。这样可以避免在组件更新时频繁的创建函数,从而提高性能。
关于上下文
上下文和useContext主要用于全局状态管理。
在React应用中,通常有很多组件需要访问相同的数据,比如用户信息、应用配置、语言环境等等。如果使用传统的props方式将这些数据传递给每个组件,代码会变得冗长且难以维护。
使用上下文和useContext可以将这些数据“注入”到组件树的某个位置,然后所有的子组件都可以通过useContext Hook来获取这些数据,而无需通过props进行传递。
另外,如果有一些需要全局化的逻辑(比如路由跳转、数据缓存等),也可以通过上下文和useContext来进行管理。
总的来说,需要使用上下文和useContext的场景主要有:
-
数据共享:当应用中有多个组件需要访问相同的数据时,使用上下文和useContext可以让所有子组件都能方便地获取到这些数据,减少了props的使用。
-
全局状态管理:当有一些需要全局化的逻辑时,可以将这些逻辑封装在上下文值中,然后在所有的子组件中都可以通过useContext来获取这些值。
-
组件封装:当组件需要封装复杂逻辑时,可以将这些逻辑封装在自定义上下文中,然后供组件内部或其他组件使用。
需要注意的是,上下文和useContext应该遵循单一职责原则,每个上下文只负责管理一组相关数据或逻辑,而不应该将不相关的数据或逻辑混在一起。
关于自定义Hook
在React中,自定义Hook是一种让我们可以在函数组件中复用逻辑的方式,以便于把逻辑抽离到可重用的函数中。
React中自定义Hook的命名规范是以use开头,这样可以方便React检测其是否为一个Hook。自定义Hook是一个函数,能够接收任意数量的参数并且能返回任何类型的值,它只是利用了React的Hook机制,让我们能够在不同的组件中共享逻辑。
例如下面这个自定义的usePrevious Hook可以在函数组件中追踪变量的上一个值:
import { useEffect, useRef } from 'react';
export function usePrevious<T>(value: T): T|undefined {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
}
在这个Hook中,我们传入一个泛型类型的变量value,返回其上一次的值。我们通过useRef Hook得到一个ref对象,在组件渲染时保存value的值,并在下一次渲染时返回上一次保存的值。
使用自定义Hook时,只需要导入自定义Hook文件并在组件中调用即可,如下所示:
import React, { useState, useEffect } from 'react';
import { usePrevious } from './usePrevious';
function Example() {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
useEffect(() => {
console.log(`Current count: ${count}, Previous count: ${prevCount}`);
}, [count, prevCount]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
在这个例子中,我们使用了自定义的usePrevious Hook来追踪count变量的上一个值prevCount,在组件的effect中输出当前count和上一个值prevCount。
一些情况下需要自定义Hook,如下:
-
抽离重复逻辑:当某些逻辑在多个组件中都有相同的重复,我们可以将这些逻辑抽离到一个自定义Hook中,以简化代码和提高可复用性。
-
分离组件关注点:当一个组件包含过多的逻辑时,我们可以将其中某些逻辑抽离到一个自定义Hook中,以使组件只关注于其主要职责。
-
管理初始值和状态:当我们需要在多个组件中管理某些共用的状态时(例如表单的初始值、URL参数的解析等),我们可以将这些状态管理逻辑封装到一个自定义Hook中。
总的来说,使用自定义Hook可以使逻辑复用变得更加简单和清晰。它是React中强大的灵活工具之一,可以提高开发效率,使代码更易于维护和调试。
关于函数式的组件和类式的组件
在React中,函数式组件和类式组件一样都可以用来定义一个组件。但是,它们的适用场景有所不同,一般建议遵循下面的简单规则:
- 当组件仅需要接收 props 并渲染简单的 UI 时,可以使用函数式组件。因为函数式组件的代码量少,逻辑清晰,易于编写和维护,同时它也能够避免 this 关键字带来的问题。例如:
function MyComponent(props) {
return <div>{props.title}</div>;
}
- 当组件需要响应用户的交互或者需要在内部管理一些状态时,必须使用类式组件。因为类式组件有更好的生命周期钩子和状态管理能力,而且可以轻松地实现一些高级特性。例如:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>Click me</button>
</div>
);
}
}
需要注意的是,随着React版本的不断更新,函数式组件的功能不断增强,已经可以支持状态的管理和一些生命周期钩子,因此官方也倾向于使用函数式组件来编写更多的业务逻辑。但是在一些特定场景下,仍然需要使用类式组件,例如需要使用生命周期钩子中的重复的渲染检查、使用ref属性等情况。