美文网首页
react知识点总结

react知识点总结

作者: 木火应 | 来源:发表于2023-03-22 18:33 被阅读0次
  1. 基本概念

1.1 什么是 React?
React 是一个用于构建用户界面的 JavaScript 库。它由 Facebook 开发,并于2013年首次发布。React 专注于组件化开发,将整个 UI 拆分成小组件,并在这些组件之间建立清晰的层次关系。通过使用 React,开发人员可以轻松地构建复杂的交互式用户界面,并提高代码的可重用性、可维护性和可测试性。
React 的核心思想是“声明式编程”,也就是说,开发人员可以专注于定义组件的外观和行为,而不必担心实现细节。React 通过引入虚拟 DOM(Virtual DOM)实现高效的 UI 更新,使得开发人员能够构建出快速响应的应用程序。
React 还可以与其他库和框架结合使用,例如 Redux、React Router 和 Next.js 等,以满足更复杂的需求。
1.2 React 的优点和缺点是什么?
1.2.1 优点
高度可重用性:组件化的思想让代码更加模块化和可复用,使得开发人员可以更加高效地编写代码。
虚拟 DOM:React 通过虚拟 DOM 的概念实现高效的 UI 更新,提高了应用的性能。
生态系统丰富:React 的生态系统非常庞大,有大量的第三方组件、库和工具,使得开发人员可以快速构建应用程序。
可扩展性:React 可以与其他框架和库进行整合,如 Redux 和 React Router 等,可以实现更加复杂的应用程序。
1.2.2 缺点
学习曲线较陡峭:React 作为一个相对复杂的前端库,需要较长的学习曲线,尤其是对于新手来说。
JSX 语法需要编译:React 使用了一种名为 JSX 的语法来描述 UI,但这种语法需要编译成普通的 JavaScript,这可能会增加应用程序的复杂度。
可维护性:由于 React 的组件化思想,开发人员需要对组件之间的关系有一个清晰的认识,否则可能会导致应用程序的可维护性下降。
状态管理:React 对于状态管理没有内置的支持,需要使用第三方库如 Redux 等来管理应用程序的状态。
1.3 React 和其他前端框架/库的区别是什么?
数据绑定方式不同:Angular 和 Vue.js 等框架采用的是双向数据绑定,即数据变化时视图也会相应地变化,而 React 采用的是单向数据流,即从父组件到子组件的数据传递方式。
组件化开发思想:React 强调组件化开发思想,将整个 UI 拆分成小组件,并在这些组件之间建立清晰的层次关系,而 Angular 和 Vue.js 等框架则更加注重整个应用程序的架构设计。
虚拟 DOM:React 通过虚拟 DOM 的概念实现高效的 UI 更新,而 Angular 和 Vue.js 等框架使用的是实际的 DOM 操作。
生命周期:React 和 Vue.js 等框架都有自己的生命周期函数,但 Angular 采用的是依赖注入和依赖注解的方式来管理组件的生命周期。
状态管理:React 对于状态管理没有内置的支持,需要使用第三方库如 Redux 等来管理应用程序的状态,而 Angular 和 Vue.js 等框架则提供了自己的状态管理机制。

  1. JSX

2.1 什么是 JSX?
2.1.1 JSX是一种在JavaScript中嵌入HTML代码的语法扩展。它是由Facebook开发的React框架所采用的一种语法,可以在JavaScript中声明和操作UI组件。
2.1.2 通过JSX,可以以一种类似HTML的方式编写UI组件,其中包含HTML标签、属性和JavaScript表达式。编写的JSX代码最终会被转换成纯JavaScript代码,然后再由React框架渲染到页面上。
以下是一个简单的示例demo,其中展示了如何使用JSX来创建一个React组件:

import React from 'react';

function MyComponent(props) {
  return (
    <div>
      <h1>{props.title}</h1>
      <p>{props.description}</p>
      <button onClick={props.onClick}>Click Me!</button>
    </div>
  );
}
export default MyComponent;

在上面的代码中,我们定义了一个名为MyComponent的React函数组件,并使用JSX语法来创建它的UI结构。组件包含一个div标签,其中包含一个h1标签、一个p标签和一个button标签。每个标签都包含了一些属性和JavaScript表达式。

2.2 JSX 和 HTML 的区别是什么?
语法标记不同:JSX 中使用的标记名称不同于 HTML 中使用的标记名称。在 JSX 中,标记名称可以是一个 React 组件的名称,或是一个原生的 HTML 标签名称。
属性使用方式不同:JSX 中的属性名称和属性值都使用双引号或单引号进行包裹。属性名称使用驼峰式命名法,而不是 HTML 中使用的短横线分隔法。
表达式的使用:JSX 中可以包含 JavaScript 表达式,这些表达式必须用花括号包裹
下面是一个简单的 JSX 示例,它定义了一个 React 组件并渲染了一个包含文本和一个属性的标签:

import React from 'react';

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

const element = <Greeting name="Alice" />;

在上面的代码中,<Greeting> 是一个 React 组件标记,它定义了一个函数组件。name 是一个属性名称,它的值是字符串 "Alice"。在 <h1> 标记中,使用了一个花括号包裹的表达式来动态地渲染 props.name。与此相比,下面是一个等价的 HTML 示例,它使用了相同的文本和属性:

<h1>Hello, Alice!</h1>

总之,JSX 提供了一种更灵活、更强大的方式来定义组件和渲染视图,它与 HTML 有很多相似之处,但也有许多重要的区别

2.3 JSX 中的事件处理和条件渲染是如何实现的?
2.3.1 事件处理
在 JSX 中,事件处理通过在元素上添加属性来实现。事件处理函数需要以 on 开头,并在其后加上对应的事件名称,如 onClick、onMouseDown 等等。事件处理函数通常需要接收一个事件对象作为参数,在函数内部可以通过事件对象来获取触发事件的元素、鼠标位置等信息。

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

export default Counter;

上面的代码中,我们创建了一个计数器组件,每次点击按钮时都会将计数器加一。我们将 handleClick 函数传递给 onClick 属性,以便在用户点击按钮时调用它。

2.3.2 条件渲染
条件渲染在 JSX 中通过使用 JavaScript 的条件语句来实现。可以使用 if、else、switch 等语句来根据不同的条件来渲染不同的内容。还可以使用三元运算符 ? : 或者逻辑与 && 来进行简单的条件判断。

import React from 'react';

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;

  if (isLoggedIn) {
    return <h1>Welcome back!</h1>;
  } else {
    return <h1>Please sign up.</h1>;
  }
}

export default Greeting;

上面的代码中,我们创建了一个问候语组件,根据用户是否已登录来显示不同的问候语。如果用户已经登录,我们渲染一个欢迎语,否则渲染一个请登录的提示。这里使用了 if-else 语句来进行条件判断。

import React from 'react';

function User(props) {
  const user = props.user;

  return (
    <div>
      <p>Name: {user.name}</p>
      {user.age && <p>Age: {user.age}</p>}
      {user.email && <p>Email: {user.email}</p>}
    </div>
  );
}

export default User;

上面的代码中,我们创建了一个用户信息组件,其中包含了用户的姓名、年龄和邮箱信息。在渲染年龄和邮箱信息时,我们使用了逻辑与 && 运算符来进行条件判断。如果用户的年龄或邮箱存在,那么相应的信息就会被渲染出来。

  1. 组件

3.1 什么是组件?
React中的组件是可复用的代码块,用于构建用户界面。它们允许将UI分解为独立且可重用的部分,从而简化代码的组织和维护。

组件可以是函数组件或类组件。函数组件是一个接收props并返回React元素的纯函数。类组件则是一个ES6类,具有状态和生命周期方法。

React组件可以包含其他React组件,从而允许构建复杂的UI层次结构。这种嵌套的结构称为组件树。每个组件都可以接收props作为其属性,并使用它们来自定义其渲染输出

使用React组件,您可以将UI分解为可重用的部分,并且通过使用相同的组件多次,可以避免重复编写相同的代码。组件的另一个优点是,它们使得应用程序更易于测试,因为每个组件都可以单独测试其行为和输出。

3.2 React 中的组件有哪些类型?
3.2.1 类组件:类组件是基于 ES6 类语法定义的组件,需要继承 React.Component 或 React.PureComponent 类,并实现 render 方法,返回用于渲染组件的 JSX 元素。下面是一个简单的类组件示例,实现了一个计数器功能:

import React, { Component } from 'react';

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

export default Counter;

    3.2.2 函数组件:函数组件是用函数定义的组件,接收一个 props 对象作为参数,并返回用于渲染组件的 JSX 元素。函数组件可以看作是纯粹的“渲染函数”,不会有自己的状态,也不会进行组件的生命周期管理。下面是一个简单的函数组件示例,实现了一个“Hello, World!”功能:

import React from 'react';

function HelloWorld(props) {
  return <div>Hello, {props.name}!</div>;
}

export default HelloWorld;

    需要注意的是,在 React 16.8 版本中,函数组件引入了 Hooks 机制,可以通过 useState、useEffect 等 Hooks 函数来管理组件状态和生命周期。因此,现在的函数组件也可以具备一定的状态管理和生命周期管理能力。
3.3 组件的生命周期是什么?
React 组件的生命周期指的是在组件被创建、更新或销毁时,React 执行的一系列方法。这些方法可以让你在不同的时间点添加代码,以执行一些自定义操作。以下是 React 组件生命周期的不同阶段:
3.3.1 Mounting 阶段:当组件第一次被创建并添加到 DOM 中时触发。

  • constructor:组件实例化时调用,可以在此方法中初始化组件的状态和绑定事件。
  • render:渲染组件并返回组件树,组件必须实现此方法。
  • componentDidMount:组件挂载后立即调用,可以在此方法中执行异步操作,例如获取数据、订阅事件等。

3.3.2 Updating 阶段

  • shouldComponentUpdate:在组件更新之前调用,可以根据新的 props 或 state 判断是否需要更新组件。
  • render:同上。
  • componentDidUpdate:在组件更新后立即调用,可以在此方法中执行一些操作,例如更新 DOM 元素。

3.3.3 Unmounting 阶段:当组件从 DOM 中移除时触发。

  • componentWillUnmount:在组件被销毁之前调用,可以在此方法中清除组件的状态、取消订阅事件等。

以下是一个简单的 React 组件,展示了不同生命周期方法的使用:

class ExampleComponent extends React.Component {
constructor(props) {
 super(props);
 this.state = {
   count: 0,
 };
 console.log('constructor');
}

componentDidMount() {
 console.log('componentDidMount');
}

componentDidUpdate(prevProps, prevState) {
 console.log('componentDidUpdate', prevProps, prevState);
}

componentWillUnmount() {
 console.log('componentWillUnmount');
}

shouldComponentUpdate(nextProps, nextState) {
 console.log('shouldComponentUpdate', nextProps, nextState);
 return true;
}

handleClick = () => {
 this.setState((prevState) => ({
   count: prevState.count + 1,
 }));
};

render() {
 console.log('render');
 return (
   <div>
     <h1>Count: {this.state.count}</h1>
     <button onClick={this.handleClick}>Increment</button>
   </div>
 );
}
}

3.4 组件通信的方式有哪些?
3.41 Props 属性传递:将数据从一个组件传递到另一个组件,可以使用 props 属性。父组件通过 props 将数据传递给子组件。子组件可以使用这些 props 来渲染自己或执行其他操作。
以下是一个示例,在父组件中定义一个名为 greeting 的 prop,将其传递给子组件并在子组件中使用它:

function ParentComponent() {
  const greeting = "Hello";
  return <ChildComponent greeting={greeting} />;
}

function ChildComponent(props) {
  return <h1>{props.greeting}, World!</h1>;
}

3.4.2 回调函数传递:在父组件中定义一个回调函数,然后将其作为 prop 传递给子组件。当子组件需要与父组件通信时,它可以调用该回调函数,并将数据作为参数传递给它。
以下是一个示例,在父组件中定义一个名为 handleClick 的回调函数,将其作为 prop 传递给子组件。当子组件中的按钮被点击时,它将调用该回调函数:

function ParentComponent() {
  function handleClick() {
    console.log("Button clicked!");
  }
  return <ChildComponent onClick={handleClick} />;
}

function ChildComponent(props) {
  return <button onClick={props.onClick}>Click me</button>;
}

3.4.3 Context 上下文传递:Context 提供了一种在组件之间共享数据的方法,无需显式地通过 props 属性传递数据。
以下是一个示例,在父组件中创建一个名为 ThemeContext 的上下文,将其包装在 Provider 组件中。然后,将数据作为 value 属性传递给 Provider。在子组件中,可以通过 useContext 钩子访问这些数据:

const ThemeContext = React.createContext("light");

function ParentComponent() {
  return (
    <ThemeContext.Provider value="dark">
      <ChildComponent />
    </ThemeContext.Provider>
  );
}

function ChildComponent() {
  const theme = useContext(ThemeContext);
  return <h1>Current theme: {theme}</h1>;
}

3.4.4 Redux 状态管理Redux 是一个流行的状态管理库,它允许在组件之间共享数据并跟踪数据的变化。使用 Redux,组件可以通过派发 actions 的方式来更新应用程序的状态,并且可以订阅状态的变化,以便在状态发生改变时更新自己。
以下是一个示例,在 Redux 中定义一个名为 counter 的状态,然后在两个组件中访问该状态:

// 定义 Redux 状态
const counterSlice = createSlice({
  name: "counter",
  initialState: 0,
  reducers: {
    increment(state) {
      return state + 1;
    },
    decrement(state) {
      return state - 1;
    },
  },
});

const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});

// 访问 Redux 状态
function ParentComponent() {
const dispatch = useDispatch();
const counter = useSelector((state) => state.counter);

function handleIncrement() {
    dispatch(counterSlice.actions.increment());
}

return (
  <div>
      <h1>Counter: {counter}</h1>
      <button onClick={handleIncrement}>Increment</button>
      <ChildComponent />
  </div>
);
}

function ChildComponent() {
    const dispatch = useDispatch();
    const counter = useSelector((state) => state.counter);

function handleDecrement() {
    dispatch(counterSlice.actions.decrement());
}

return (
<div>
    <h2>Counter in child component: {counter}</h2>
    <button onClick={handleDecrement}>Decrement</button>
</div>
);
}

在这个示例中,我们使用 Redux 定义了一个名为 counter 的状态,并在两个组件中访问了该状态。我们在 ParentComponent 中定义了一个 handleIncrement 函数来增加 counter 状态的值,并将其传递给子组件 ChildComponent。在 ChildComponent 中,我们定义了一个 handleDecrement 函数来减少 counter 状态的值。当我们在任何一个组件中更新 counter 状态时,其他组件都会立即更新显示的值。

注意,为了使用 Redux,我们需要在组件中引入相应的库,并使用 useDispatchuseSelector 钩子来派发 actions 和访问状态。

  1. 状态管理
    4.1 React 的状态是什么?
    React 的状态(state)是一个对象,包含组件内部可以修改和管理的数据。状态通常用于在组件中存储用户交互的数据、网络请求返回的数据等动态数据。每当状态发生变化时,React 会重新渲染组件以反映新的状态。
    状态可以通过 useState 钩子进行创建和管理。该钩子接受一个初始值,并返回一个数组,包含当前状态的值和一个更新状态的函数。当更新状态的函数被调用时,React 会重新渲染组件,并将新的状态值传递给组件。
    以下是一个示例,演示如何使用 useState 钩子来创建和管理状态:
import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

在这个示例中,我们使用 useState 钩子创建了一个名为 count 的状态,初始值为 0。当按钮被点击时,我们调用 setCount 函数来更新状态的值,从而触发组件的重新渲染。每次组件重新渲染时,我们显示当前状态的值。

4.2 如何管理组件的状态?

4.2.1 使用 useState 钩子创建状态:React 可以使用组件状态(State)来管理组件内部的数据。通过在组件中定义状态,我们可以轻松地在组件的生命周期内管理组件内部的数据,并在需要时进行更新。
在 React 中,我们可以使用 useState 钩子来创建和管理组件的状态。 useState 钩子接受一个初始值,返回一个数组,包含当前状态值和一个更新状态值的函数。
以下是一个示例,演示如何使用 useState 钩子来管理组件的状态:

import React, { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount(count + 1);
  }

  function decrement() {
    setCount(count - 1);
  }

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

在这个示例中,我们使用 useState 钩子创建了一个名为 count 的状态,初始值为 0。我们定义了两个函数 incrementdecrement 来更新状态值。当用户点击增加按钮时,我们调用 increment 函数来更新状态的值。同样,当用户点击减少按钮时,我们调用 decrement 函数来更新状态的值。每次状态值被更新时,组件会自动重新渲染,并显示更新后的状态值。
在实际应用中,我们可以将状态作为组件属性传递给其他组件,或者在组件的生命周期中使用它来处理数据和用户交互。使用组件状态可以使我们轻松地管理组件内部的数据,并保持组件的可重用性和可维护性。

4.2.2 不要直接修改状态: 在 React 中,状态应该被视为不可变的。直接修改状态的值是不安全的,并且可能会导致意想不到的行为。使用 setState 或传递一个新的状态值来更新状态,而不是修改现有的状态。

// 错误的示例
function handleClick() {
  // 直接修改状态
  count += 1;
  setCount(count);
}

// 正确的示例
function handleClick() {
  // 传递新的状态值
  setCount(count + 1);
}

4.2.3 状态更新可能是异步的: React 可能会将多个 setState 调用批量处理为单个更新。这是为了提高性能并防止不必要的重新渲染。因此,状态更新可能是异步的。如果需要在状态更新后执行某些操作,可以使用 useEffect 钩子来注册一个副作用。useEffect 钩子的回调函数会在组件渲染后异步执行。

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>
  );
}

在这个例子中,我们使用 useEffect 钩子注册了一个副作用函数,它会在组件渲染后异步执行。每次状态 count 的值发生变化时,副作用函数都会重新执行。在这个例子中,我们将文档标题设置为 You clicked x times,其中 x 是状态 count 的当前值。
4.2.4 避免过多的状态: 在设计 React 组件时,应该尽量减少组件所需的状态数量。如果有太多的状态,组件会变得难以维护,并且可能会导致意外的行为。一种常见的方法是将状态移动到父组件中,并通过 props 传递给子组件。这种方式通常称为“提升状态”。

import React, { useState } from "react";

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <Child count={count} setCount={setCount} />
    </div>
  );
}

function Child(props) {
  const { count, setCount } = props;

  return (
    <button onClick={() => setCount(count + 1)}>Click me</button>
  );
}

在这个例子中,我们将状态 count 和更新函数 setCount 作为 props 传递给子组件 Child。在 Child 中,我们可以通过 props 访问这些值,并在按钮点击时调用更新函数来更新状态。
4.2.5 使用不可变的数据结构: React 应用程序通常涉及大量的状态更新,因此使用不可变的数据结构可以使应用程序更可预测且更易于调试。使用不可变数据结构,可以保证每个组件都具有自己的数据副本,而不是共享相同的状态。一些流行的不可变数据结构包括 Immutable.js 和 Immer。在使用这些库时,需要注意 API 的不同之处。

import React, { useState } from "react";
import produce from "immer";

function Example() {
  const [items, setItems] = useState([]);

  function addItem(item) {
    setItems(
      produce(items, (draftItems) => {
        draftItems.push(item);
      })
    );
  }

  return (
    <div>
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <button onClick={() => addItem("New Item")}>Add Item</button>
    </div>
  );
}

在这个例子中,我们使用 Immer 库来管理数组状态。我们使用 produce 函数创建一个新的不可变数组,然后使用 push 方法将新项添加到数组中。通过这种方式,我们可以确保状态是不可变的,并且可以避免不必要的重新渲染。

总之,使用适当的方式管理组件的状态可以使组件更加可维护、更容易调试,并提高应用程序的性能和稳定性。在设计组件时,应尽量减少状态的数量,并使用不可变的数据结构来确保状态不可变。

使用 useCallback 和 memo 钩子函数进行性能优化,可以避免不必要的重新渲染,提高应用程序的性能。

import React, { useState, useCallback, memo } from "react";
import produce from "immer";

function Example() {
  const [items, setItems] = useState([]);

  const memoizedAddItem = useCallback((item) => {
    setItems(
      produce(items, (draftItems) => {
        draftItems.push(item);
      })
    );
  }, [setItems, items]);

  return (
    <div>
      <ul>
        {items.map((item, index) => (
          <Item key={index} item={item} />
        ))}
      </ul>
      <button onClick={() => memoizedAddItem("New Item")}>Add Item</button>
    </div>
  );
}

const Item = memo(({ item }) => {
  console.log("Rendering Item"); // 输出每次重新渲染时的日志
  return <li>{item}</li>;
});

4.3 如何在组件之间共享状态?
React 在组件之间共享状态的方式主要有两种:通过 props 传递状态和使用 Context API
4.3.1 通过 props 传递状态: 在 React 中,父组件可以通过 props 将状态传递给子组件。子组件可以通过 props 获取父组件传递过来的状态,从而实现在组件之间共享状态的目的。这种方式适用于组件之间的嵌套比较浅,状态需要向下传递的情况。

import React, { useState } from "react";

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Parent Component</h1>
      <p>Count: {count}</p>
      <Child count={count} setCount={setCount} />
    </div>
  );
}

function Child({ count, setCount }) {
  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h2>Child Component</h2>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
}

在这个示例中,Parent 组件包含一个计数器状态 count,它将 count 状态通过 props 传递给 Child 组件。Child 组件可以通过 props 获取 count 状态,并将 setCount 函数作为 props 传递回 Parent 组件,从而实现了在组件之间共享状态的目的。

4.3.2 使用 Context API: Context API 是 React 提供的一种在组件之间共享状态的方式。通过创建一个 Context 对象,可以将状态提供给整个应用程序中的组件使用,从而避免了通过 props 一层层传递状态的麻烦。

import React, { useState, createContext, useContext } from "react";

const CountContext = createContext();

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Parent Component</h1>
      <p>Count: {count}</p>
      <CountContext.Provider value={{ count, setCount }}>
        <Child />
      </CountContext.Provider>
    </div>
  );
}

function Child() {
  const { count, setCount } = useContext(CountContext);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h2>Child Component</h2>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
}
  1. 路由

5.1 什么是 React 路由?
React 路由是用于在 React 单页应用程序(SPA)中实现页面导航和路由管理的库。它允许我们在应用程序中定义 URL 路由、链接、页面传递参数等功能。
React 路由主要由以下几个组件构成:

  • BrowserRouter:用于定义一个包含所有路由的容器,通常放在应用程序的最外层。
import { BrowserRouter, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";

function App() {
  return (
    <BrowserRouter>
      <div>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
      </div>
    </BrowserRouter>
  );
}

在上面的示例中,我们通过 BrowserRouter 定义了一个包含所有路由的容器,并在容器中定义了两个路由:一个是路径为 / 的首页路由,另一个是路径为 /about 的关于页面路由。这里使用了 exact 属性来确保只有在路径完全匹配时才会渲染对应的组件。

  • Route:用于定义每个路由及其对应的组件,通常作为 BrowserRouter 的子组件。
import { Route } from "react-router-dom";
import Home from "./components/Home";

function App() {
  return (
    <div>
      <Route path="/" component={Home} />
    </div>
  );
}
  • Link:用于定义链接,点击链接时会触发路由切换,通常用于导航菜单和页面内部链接。
import { Link } from "react-router-dom";

function Menu() {
  return (
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      </li>
    </ul>
  );
}

  • Switch:用于在多个路由匹配时选择第一个匹配的路由。
import { Switch, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import NotFound from "./components/NotFound";

function App() {
  return (
    <div>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
        <Route component={NotFound} />
      </Switch>
    </div>
  );
}

在上面的示例中,我们使用了 Switch 组件来在多个路由匹配时选择第一个匹配的路由。这样,如果用户访问了不存在的路由,就会渲染 NotFound 组件,而不是默认的 Home 组件。

React 路由可以帮助我们在单页应用程序中实现多页面的效果,同时也支持使用参数、嵌套路由等高级特性。使用 React 路由可以使我们的应用程序更易于维护和扩展,同时也能够提升用户体验。

5.2 React 路由的优点是什么?
5.2.1 单页应用:React 路由可以帮助我们在单页应用中实现多页面的效果,从而提升用户体验和应用的响应速度。

import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Home} />
        <Route path="/about" component={About} />
      </div>
    </BrowserRouter>
  );
}

export default App;

5.2.3 嵌套路由:React 路由支持嵌套路由,可以帮助我们实现复杂的应用程序结构,从而使得代码更加清晰、易于维护和扩展。

import React from 'react';
import { BrowserRouter, Route, Link } from 'react-router-dom';

function Home() {
  return <h2>Home</h2>;
}

function Products() {
  return (
    <div>
      <h2>Products</h2>
      <ul>
        <li>
          <Link to="/products/1">Product 1</Link>
        </li>
        <li>
          <Link to="/products/2">Product 2</Link>
        </li>
      </ul>
      <Route path="/products/:id" component={Product} />
    </div>
  );
}

function Product({ match }) {
  return <h3>Product ID: {match.params.id}</h3>;
}

function App() {
  return (
    <BrowserRouter>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/products">Products</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Home} />
        <Route path="/products" component={Products} />
      </div>
    </BrowserRouter>
  );
}

export default App;

5.2.4 参数路由:React 路由支持参数路由,可以帮助我们实现动态的页面效果,例如搜索功能或者用户详情页等。

import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

function Home() {
  return <h2>Home</h2>;
}

function User({ match }) {
  return <h2>User: {match.params.username}</h2>;
}

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/users/john">User John</Link>
            </li>
            <li>
              <Link to="/users/jane">User Jane</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/" exact component={Home} />
          <Route path="/users/:username" component={User} />
        </Switch>
      </div>
    </Router>
  );
}

export default App;

5.2.5 代码分割:React 路由支持代码分割,可以帮助我们将应用程序代码按照路由进行拆分,从而实现更快的页面加载速度和更好的性能。

import React, { lazy, Suspense } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";

const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
const Contact = lazy(() => import("./Contact"));

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/contact">Contact</Link>
            </li>
          </ul>
        </nav>

        <Suspense fallback={<div>Loading...</div>}>
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/about" component={About} />
            <Route path="/contact" component={Contact} />
          </Switch>
        </Suspense>
      </div>
    </Router>
  );
}

export default App;

5.2.6 社区支持:React 路由是 React 生态系统中广泛使用的一个库,具有丰富的文档和社区支持,可以帮助我们快速构建高质量的应用程序。

以上分别给出了相关的示例demo,实际react还有其他非常丰富的扩展,具体请访问react官网
)

5.3 如何在 React 中使用路由?
要在 React 中使用路由,可以使用 React Router 库。下面是一个简单的示例demo,演示如何在 React 中使用路由。

  • 首先,需要安装 React Router 库:
npm install react-router-dom
  • 然后,可以创建一个基本的路由组件,例如:
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

export default App;

在上面的示例中,我们使用了 BrowserRouter 组件作为路由容器,它会使用 HTML5 history API 来实现 URL 路径的变化。然后,我们在 nav 中创建了链接,分别指向 Home、About 和 Users 组件。使用 Link 组件创建的链接会在点击时自动更新 URL,并使用 Route 组件来定义 URL 和对应的组件。最后,使用 Switch 组件来包裹所有的 Route 组件,以确保只有一个路由被匹配。

可以看到,React Router 库提供了一组简单的 API 来实现路由功能,使得我们可以轻松地在 React 应用中添加路由功能。

扩展:

  • 鉴权控制: React 路由可以用于实现登录鉴权控制,例如对需要登录才能访问的页面进行控制。
import { Route, Redirect } from "react-router-dom";
import { useAuth } from "./authContext";

function PrivateRoute({ component: Component, ...rest }) {
  const { isAuthenticated } = useAuth();
  return (
    <Route
      {...rest}
      render={(props) =>
        isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />
      }
    />
  );
}

其中的authContext.js大概长这样:

import React from 'react';

const AuthContext = React.createContext({
  isAuthenticated: false,
  toggleAuth: () => {},
});

export default AuthContext;
  • 多语言支持: React 路由可以用于实现多语言支持,例如根据不同的语言选择相应的页面进行展示。
import { Route, Redirect } from "react-router-dom";
import { useLanguage } from "./languageContext";

function LocalizedRoute({ path, localizedPaths, component: Component }) {
  const { language } = useLanguage();
  const localizedPath = localizedPaths[language];
  const to = localizedPath ? localizedPath : "/not-found";
  return <Route exact path={path} component={localizedPath ? Component : Redirect to={to}} />;
}

其中languageContext.js大概长这样:

import React from 'react';

const LanguageContext = React.createContext({
  language: 'en',
  setLanguage: () => {},
});

export default LanguageContext;

其中localizedPaths.js内容大概长这样:

const localizedPaths = {
  '/': {
    en: '/',
    es: '/es/',
    zh: '/zh/',
  },
  '/about': {
    en: '/about',
    es: '/es/about',
    zh: '/zh/about',
  },
  '/contact': {
    en: '/contact',
    es: '/es/contacto',
    zh: '/zh/lianxi',
  },
  // ...
};

export default localizedPaths;

这个文件定义了一个对象 localizedPaths,它包含不同语言下的页面路径。在这个示例中,有英语、西班牙语和中文三种语言。当用户更改当前语言时,应用程序可以使用这个对象中的路径来渲染正确的页面。

  1. 高阶组件

6.1 高阶组件是什么?
React高阶组件是一种复用组件逻辑的技术,它本质上是一个函数,接受一个组件作为参数,并返回一个新的增强组件。通过这种方式,可以将某个组件所需的逻辑和状态抽离出来,封装到一个高阶组件中,让多个组件都可以复用这些逻辑和状态。

具体来说,高阶组件接受一个组件作为参数,并返回一个新的增强组件。这个增强组件可以继承原组件的props,还可以添加一些额外的props,例如状态、事件处理函数等。通常情况下,高阶组件会对原组件进行一些装饰,例如添加一些生命周期方法、渲染逻辑等。在返回的增强组件中,也可以选择性地将一部分props传递给原组件,让原组件能够使用这些props。

import React from "react";

function withTimer(Component) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        time: new Date(),
      };
    }

    componentDidMount() {
      this.interval = setInterval(() => {
        this.setState({ time: new Date() });
      }, 1000);
    }

    componentWillUnmount() {
      clearInterval(this.interval);
    }

    render() {
      return <Component time={this.state.time} {...this.props} />;
    }
  };
}

function DisplayTime(props) {
  const { time } = props;
  return (
    <div>
      <p>The current time is:</p>
      <p>{time.toLocaleTimeString()}</p>
    </div>
  );
}

const DisplayTimeWithTimer = withTimer(DisplayTime);

function App() {
  return <DisplayTimeWithTimer />;
}

export default App;

在这个例子中,withTimer 是一个高阶组件,它接受一个组件作为参数,并返回一个新的组件。这个新组件通过在组件挂载时启动计时器来更新当前时间。然后,它通过 props 将当前时间传递给原始组件。在 App 组件中,我们使用了 DisplayTimeWithTimer 这个包装后的组件,它可以显示当前时间并在每秒更新一次。

React高阶组件可以帮助我们实现组件逻辑的复用,提高代码的可读性和可维护性。常见的使用场景包括:
6.1.1 实现组件的懒加载(Code-Splitting);

import React, { Component } from 'react';

function asyncComponent(getComponent) {
  return class AsyncComponent extends Component {
    static Component = null;
    state = { Component: AsyncComponent.Component };

    componentWillMount() {
      if (!this.state.Component) {
        getComponent().then(Component => {
          AsyncComponent.Component = Component;
          this.setState({ Component });
        });
      }
    }

    render() {
      const { Component } = this.state;
      if (Component) {
        return <Component {...this.props} />;
      }
      return null;
    }
  };
}

// 代码分割
const Home = asyncComponent(() => import('./Home'));

class App extends Component {
  render() {
    return (
      <div>
        <h1>Hello, World!</h1>
        <Home />
      </div>
    );
  }
}

上面的代码中,asyncComponent 是一个高阶组件,它接收一个函数 getComponent 作为参数,该函数返回一个动态加载的组件。在 AsyncComponent 中,我们维护了一个 Component 状态,用于保存动态加载的组件,如果当前组件已经加载,则直接渲染该组件;否则,我们调用 getComponent 函数加载组件,并将加载到的组件保存在 Component 状态中,再进行渲染。
在示例代码中,我们使用 asyncComponent 高阶组件来动态加载 Home 组件,实现了组件的懒加载和代码分割。

6.1.2 处理表单的输入和验证逻辑;

import React, { Component } from "react";

function withForm(WrappedComponent) {
  return class extends Component {
    state = {
      values: {},
      errors: {},
    };

    handleChange = (e) => {
      const { name, value } = e.target;

      this.setState((prevState) => ({
        values: {
          ...prevState.values,
          [name]: value,
        },
      }));
    };

    handleSubmit = (e) => {
      e.preventDefault();

      const { values } = this.state;

      // validate form
      const errors = {};
      let hasError = false;

      Object.keys(values).forEach((key) => {
        const value = values[key];

        if (!value) {
          errors[key] = `${key} is required`;
          hasError = true;
        }
      });

      if (hasError) {
        this.setState({ errors });
      } else {
        console.log("submit form with values:", values);
      }
    };

    render() {
      const { values, errors } = this.state;

      return (
        <WrappedComponent
          values={values}
          errors={errors}
          handleChange={this.handleChange}
          handleSubmit={this.handleSubmit}
        />
      );
    }
  };
}

class MyForm extends Component {
  render() {
    const { values, errors, handleChange, handleSubmit } = this.props;

    return (
      <form onSubmit={handleSubmit}>
        <div>
          <label htmlFor="name">Name</label>
          <input
            type="text"
            name="name"
            value={values.name || ""}
            onChange={handleChange}
          />
          {errors.name && <span>{errors.name}</span>}
        </div>
        <div>
          <label htmlFor="email">Email</label>
          <input
            type="email"
            name="email"
            value={values.email || ""}
            onChange={handleChange}
          />
          {errors.email && <span>{errors.email}</span>}
        </div>
        <div>
          <label htmlFor="password">Password</label>
          <input
            type="password"
            name="password"
            value={values.password || ""}
            onChange={handleChange}
          />
          {errors.password && <span>{errors.password}</span>}
        </div>
        <button type="submit">Submit</button>
      </form>
    );
  }
}

const FormWithForm = withForm(MyForm);

export default FormWithForm;

在这个示例代码中,我们定义了一个高阶组件 withForm,它接受一个组件作为参数,并返回一个新的组件。
这个高阶组件将表单的输入和验证逻辑封装在内部,通过 handleChange 和 handleSubmit 方法来更新表单的状态和处理表单的提交。
我们可以将任何需要处理表单的输入和验证逻辑的组件传递给 withForm,然后在被包装的组件中通过 props 获取表单的状态和处理表单的方法。
在这个示例代码中,我们使用 MyForm 组件作为被包装的组件,并将其作为 withForm 函数的参数传递

6.1.3 处理组件的状态管理逻辑;

import React, { useState } from "react";

function withCounter(WrappedComponent) {
  return function WithCounter(props) {
    const [count, setCount] = useState(0);

    function incrementCount() {
      setCount(count + 1);
    }

    return (
      <WrappedComponent
        {...props}
        count={count}
        incrementCount={incrementCount}
      />
    );
  };
}

function Counter(props) {
  return (
    <div>
      <h2>Count: {props.count}</h2>
      <button onClick={props.incrementCount}>Increment</button>
    </div>
  );
}

const CounterWithCounter = withCounter(Counter);

function App() {
  return (
    <div>
      <CounterWithCounter />
    </div>
  );
}

上述代码中,withCounter 是一个接受一个组件作为参数的高阶组件。它返回了一个新的函数 WithCounter,这个函数渲染了原来传入的组件 WrappedComponent,并且还定义了一个 count 状态和一个 incrementCount 函数来管理组件的状态。
Counter 组件是一个简单的展示计数器和按钮的组件,它接受两个 props:count 和 incrementCount,这些 props 都是通过 withCounter 高阶组件传递进来的。
最后,CounterWithCounter 组件是使用 withCounter 高阶组件对 Counter 组件进行包装后得到的新组件。在 App 组件中,我们渲染了 CounterWithCounter 组件,这个组件就可以正常地使用 count 和 incrementCount 这两个 props 来管理状态了。

6.1.4 实现组件的权限控制等。

import React from 'react';

// 假设有一个用于验证用户是否有某个权限的函数
const checkPermission = (user, permission) => {
  return user && user.permissions && user.permissions.includes(permission);
}

// 权限控制高阶组件
const withAuthorization = (WrappedComponent, permission) => {
  return class extends React.Component {
    render() {
      const { user } = this.props;

      if (checkPermission(user, permission)) {
        // 用户有权限,渲染原始组件
        return <WrappedComponent {...this.props} />;
      } else {
        // 用户没有权限,显示未授权消息或重定向到登录页面等
        return <div>没有权限访问</div>;
      }
    }
  }
}

// 普通组件
const MyComponent = (props) => {
  return (
    <div>
      <h1>欢迎来到我的组件</h1>
      <p>只有拥有特定权限的用户才能看到这里的内容。</p>
    </div>
  );
}

// 使用 withAuthorization 高阶组件包装 MyComponent 组件
const MyComponentWithAuth = withAuthorization(MyComponent, 'can_view_sensitive_content');

// 父组件包含用户信息,传递给子组件
class App extends React.Component {
  state = {
    user: {
      name: 'John Doe',
      permissions: ['can_view_sensitive_content'],
    },
  };

  render() {
    return (
      <div>
        <MyComponentWithAuth user={this.state.user} />
      </div>
    );
  }
}

export default App;

上面的示例中,我们定义了一个 checkPermission 函数,它用于验证用户是否拥有某个权限。接着,我们定义了一个名为 withAuthorization 的高阶组件,它将 MyComponent 组件作为参数,并使用传入的 permission 验证用户是否有权限。如果用户拥有该权限,withAuthorization 组件渲染 MyComponent,否则渲染一个提示消息。最后,我们将 MyComponent 组件传递给 withAuthorization 函数,返回一个具有权限控制功能的新组件 MyComponentWithAuth。在 App 组件中,我们将 MyComponentWithAuth 组件包装在 div 中,将用户信息作为 props 传递给它。

当用户拥有 can_view_sensitive_content 权限时,将会看到 MyComponent 组件的内容。否则,将会看到一个提示消息。这是一个简单的示例,你可以根据自己的需要进行修改和扩展。

6.2 渲染属性模式是什么?
React 的渲染属性模式是一种将组件渲染成函数的方法,通过这种方法,我们可以将一个组件渲染为一个函数,并将该函数的参数传递给该组件。这种模式使我们可以更容易地将状态和行为传递给子组件,并使组件更容易测试和重用。
在渲染属性模式下,我们可以将组件视为一个函数,该函数的输入为 props,输出为渲染的元素。在渲染属性模式下,我们可以将组件定义为一个函数,并将 props 作为该函数的参数,该函数返回一个 React 元素。这种模式可以让我们更容易地将状态和行为传递给子组件,并使组件更容易测试和重用。

import React, { useState } from "react";

function Input({ label, value, onChange }) {
  return (
    <div>
      <label>{label}</label>
      <input type="text" value={value} onChange={onChange} />
    </div>
  );
}

function App() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");

  function handleNameChange(event) {
    setName(event.target.value);
  }

  function handleEmailChange(event) {
    setEmail(event.target.value);
  }

  return (
    <div>
      <Input label="Name" value={name} onChange={handleNameChange} />
      <Input label="Email" value={email} onChange={handleEmailChange} />
    </div>
  );
}

export default App;

在这个示例中,我们定义了一个 Input 组件,它接受 label、value 和 onChange 这三个属性。我们还定义了一个 App 组件,它使用 Input 组件来渲染两个输入框,其中一个输入框是用来输入名字,另一个是用来输入邮箱地址的。

通过渲染属性模式,我们可以将 Input 组件定义为一个函数,该函数接受 label、value 和 onChange 三个属性,返回一个元素。在 App 组件中,我们可以将 Input 组件作为一个函数来调用,并将需要的属性传递给它。这种模式可以让我们更容易地将状态和行为传递给子组件,并使组件更容易测试和重用。

6.3 React Hooks 是什么?
React Hooks 是 React 16.8 引入的一个特性,它可以让开发者在不编写 class 组件的情况下,使用 state、生命周期等特性。Hooks 是一组 React API,它们可以让你在函数组件中“钩入” React state 及生命周期等特性,使得你可以在函数组件中编写类似于 class 组件的逻辑。

使用 React Hooks 可以帮助开发者更加容易地编写和重用状态逻辑,避免组件之间共享状态所带来的麻烦。常见的 Hooks 包括 useState、useEffect、useContext 等等。这些 Hooks 都可以在函数组件中使用,从而使得函数组件的能力得到了大幅度提升。

  • useState是 React 中最基础、最常用的 Hook 之一,它可以让函数组件拥有状态。下面是一个计数器示例:
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • useEffect 用于处理函数组件中的副作用(如异步请求、DOM 操作等)。下面是一个简单的示例,当组件渲染时会发起一个异步请求,请求成功后更新状态:
import React, { useState, useEffect } from 'react';

function FetchData() {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
      const json = await response.json();
      setData(json);
    }

    fetchData();
  }, []);

  if (!data) {
    return <div>Loading...</div>;
  }

  return <div>{data.title}</div>;
}
  • useContext 可以在函数组件中使用上下文。下面是一个简单的示例,使用 createContext 和 useContext 创建一个主题上下文,让组件根据主题进行渲染:
import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemeButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme }}>I am styled by theme context!</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemeButton />
    </ThemeContext.Provider>
  );
}
  • useCallback 可以缓存函数,避免因为函数的引用不一致导致组件重新渲染。下面是一个简单的示例,在组件中使用 useCallback 缓存函数,防止传递给子组件时因为不一致导致子组件不必要的重新渲染:
import React, { useState, useCallback } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <Child onClick={handleClick} />
    </div>
  );
}

function Child({ onClick }) {
  console.log('child rendered');
  return <button onClick={onClick}>Click me</button>;
}
  • useMemo用于缓存计算结果的引用,避免在每次渲染时都重新计算;
import React, { useState, useMemo } from 'react';

function SquareRoot({ number }) {
  // 使用 useMemo 缓存计算结果
  const squareRoot = useMemo(() => {
    console.log('Calculating square root...');
    return Math.sqrt(number);
  }, [number]);

  return (
    <div>
      The square root of {number} is {squareRoot}
    </div>
  );
}

function Example() {
  const [number, setNumber] = useState(0);

  return (
    <div>
      <input type="number" value={number} onChange={e => setNumber(e.target.value)} />
      <SquareRoot number={number} />
    </div>
  );
}

注意,useMemo 并不一定会带来性能上的提升,因为缓存的值需要计算,而这个计算过程可能会比直接重新计算要慢。只有在计算成本比较高,而且计算结果不经常改变的情况下,才建议使用 useMemo。

  • useRef:用于在函数组件中缓存任意值,包括 DOM 节点、计时器 ID 等;
import React, { useRef, useEffect } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return (
    <div>
      <label>
        Name:
        <input type="text" ref={inputRef} />
      </label>
    </div>
  );
}
  • 当组件需要管理复杂的状态逻辑时,可以使用 React 提供的 useReducer Hook 来处理状态。useReducer 接受一个 reducer 函数和初始状态作为参数,并返回一个包含当前状态和一个 dispatch 函数的数组。dispatch 函数可以触发状态的更新,并且根据传入的 action 参数执行 reducer 函数来更新状态。
import React, { useReducer } from "react";

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, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </div>
  );
}

在上面的示例代码中,我们定义了一个 reducer 函数,它接受当前状态和一个 action 参数,并根据 action 的类型来更新状态。我们还定义了一个 Counter 组件,它使用 useReducer Hook 来初始化状态,并使用 dispatch 函数来触发状态的更新。当用户点击“+”按钮时,我们会调用 dispatch 函数,并传入一个类型为 “increment” 的 action 参数,从而触发 reducer 函数来增加计数器的值。

使用 useReducer 可以让组件更加易于维护和测试,因为它可以将复杂的状态逻辑和 UI 逻辑分离开来。当然,在使用 useReducer 时需要注意遵循一些最佳实践,比如将 reducer 函数放在组件外部定义、避免直接修改 state 等。

6.4 使用 Context API 实现跨组件通信。
当需要在 React 应用程序的多个组件之间共享数据时,Context API 是一种非常有用的工具。下面是一个使用 Context API 在 React 中实现跨组件通信的示例。

假设我们有一个名为 App 的组件,它包含两个子组件 ComponentA 和 ComponentB。我们想要在这两个子组件之间共享一个状态值。

首先,我们需要创建一个新的 context 对象。可以在 App 组件中使用 React.createContext 方法创建该对象。

// App.js

import React from 'react';

export const MyContext = React.createContext();

然后,我们需要将状态值存储在 App 组件的状态中,并将其传递给子组件。我们可以使用 MyContext.Provider 组件将状态值提供给子组件。

// App.js

import React, { useState } from 'react';
import { MyContext } from './MyContext';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';

function App() {
  const [count, setCount] = useState(0);

  return (
    <MyContext.Provider value={{ count, setCount }}>
      <div className="App">
        <ComponentA />
        <ComponentB />
      </div>
    </MyContext.Provider>
  );
}

export default App;

现在,我们可以在 ComponentA 和 ComponentB 中访问这个 context 对象。在 ComponentA 中,我们可以使用 useContext 钩子来获取该对象,然后可以使用其中的状态值。同样,我们也可以在 ComponentB 中获取并使用该值。

// ComponentA.js

import React, { useContext } from 'react';
import { MyContext } from './MyContext';

function ComponentA() {
  const { count, setCount } = useContext(MyContext);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h2>Component A</h2>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment Count</button>
    </div>
  );
}

export default ComponentA;
// ComponentB.js

import React, { useContext } from 'react';
import { MyContext } from './MyContext';

function ComponentB() {
  const { count } = useContext(MyContext);

  return (
    <div>
      <h2>Component B</h2>
      <p>Count: {count}</p>
    </div>
  );
}

export default ComponentB;

现在,当我们在 ComponentA 中点击按钮时,count 的值将增加,并在 ComponentB 中更新。这是因为它们都使用了同一个 context 对象来共享数据。

相关文章

  • 来自一位react新手的react入门须知

    前言:自己刚刚总结的关于react的知识点,比较简单,大家可以粗略看看。 一:关于react的一些知识点 1,Js...

  • React.js 入门学习笔记

    React.js 知识点总结: 之前已大量使用过 Vue.js 所以对于学习 React.js 有所帮助,看起来还...

  • React学习总结(参考官方文档)

    React学习总结 参考官方文档,整理一些应该被重视的知识点 React中的事件相关 类似HTML中事件的使用方法...

  • React 知识点总结

    #React框架的学习# React的起源和发展 起初facebook在建设instagram(图片分享)的时候因...

  • React知识点总结

    1.keys的作用是什么? keys是react用于追踪列表元素被修改,添加或移除的标识,我们需要保证元素的key...

  • React学习笔记(一)

    知识点及简介 知识点 简介 React JS :使用React的语法来编写一些网页的交互效果 React Nati...

  • React-Native实现登录界面

    最近,在用React Native开发,以下是一个登录界面,在登录界面用到的知识点现总结如下: 效果图:

  • react 小知识点总结

    1.获取表单的所有数据 2. 获取表单的部分字段数据 ES6中Array.find()和findIndex()函数...

  • react 知识点简单总结

    1.react.js 、react-dom.js 和 Browser.js ,它们必须首先加载。其中,react....

  • 学习react总结知识点

    传统HTML中 handleclick函数自动绑定了this,而react中 需要手动绑定,下面是回调函数绑定...

网友评论

      本文标题:react知识点总结

      本文链接:https://www.haomeiwen.com/subject/qmcbrdtx.html