Skip to main content

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语言是如何工作的

  • https://babeljs.io/repl

关于组件根节点

  • 组件只允许有一个根节点元素,如果需要多个的话可以使用 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的场景主要有:

  1. 数据共享:当应用中有多个组件需要访问相同的数据时,使用上下文和useContext可以让所有子组件都能方便地获取到这些数据,减少了props的使用。

  2. 全局状态管理:当有一些需要全局化的逻辑时,可以将这些逻辑封装在上下文值中,然后在所有的子组件中都可以通过useContext来获取这些值。

  3. 组件封装:当组件需要封装复杂逻辑时,可以将这些逻辑封装在自定义上下文中,然后供组件内部或其他组件使用。

需要注意的是,上下文和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,如下:

  1. 抽离重复逻辑:当某些逻辑在多个组件中都有相同的重复,我们可以将这些逻辑抽离到一个自定义Hook中,以简化代码和提高可复用性。

  2. 分离组件关注点:当一个组件包含过多的逻辑时,我们可以将其中某些逻辑抽离到一个自定义Hook中,以使组件只关注于其主要职责。

  3. 管理初始值和状态:当我们需要在多个组件中管理某些共用的状态时(例如表单的初始值、URL参数的解析等),我们可以将这些状态管理逻辑封装到一个自定义Hook中。

总的来说,使用自定义Hook可以使逻辑复用变得更加简单和清晰。它是React中强大的灵活工具之一,可以提高开发效率,使代码更易于维护和调试。

关于函数式的组件和类式的组件

在React中,函数式组件和类式组件一样都可以用来定义一个组件。但是,它们的适用场景有所不同,一般建议遵循下面的简单规则:

  1. 当组件仅需要接收 props 并渲染简单的 UI 时,可以使用函数式组件。因为函数式组件的代码量少,逻辑清晰,易于编写和维护,同时它也能够避免 this 关键字带来的问题。例如:
function MyComponent(props) {
return <div>{props.title}</div>;
}
  1. 当组件需要响应用户的交互或者需要在内部管理一些状态时,必须使用类式组件。因为类式组件有更好的生命周期钩子和状态管理能力,而且可以轻松地实现一些高级特性。例如:
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属性等情况。