- 基本概念
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 等框架则提供了自己的状态管理机制。
- 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;
上面的代码中,我们创建了一个用户信息组件,其中包含了用户的姓名、年龄和邮箱信息。在渲染年龄和邮箱信息时,我们使用了逻辑与 && 运算符来进行条件判断。如果用户的年龄或邮箱存在,那么相应的信息就会被渲染出来。
- 组件
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
,我们需要在组件中引入相应的库,并使用 useDispatch
和 useSelector
钩子来派发 actions
和访问状态。
-
状态管理
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。我们定义了两个函数 increment
和 decrement
来更新状态值。当用户点击增加按钮时,我们调用 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>
);
}
- 路由
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,它包含不同语言下的页面路径。在这个示例中,有英语、西班牙语和中文三种语言。当用户更改当前语言时,应用程序可以使用这个对象中的路径来渲染正确的页面。
- 高阶组件
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 对象来共享数据。
网友评论