10 使用 Hooks 管理状态

10 使用 Hooks 管理状态

在学习 React 的过程中,Hooks 是一个非常重要的特性,特别是 useStateuseEffect,它们为函数组件带来了更加灵活的状态管理和副作用处理。本节将详细介绍如何使用这些 Hooks 来管理组件状态。

1. 什么是 Hooks?

Hooks 是在 React 16.8 中引入的特性,使得在函数组件中使用状态和其他 React 特性变得更加简单。最常用的两个 Hooks 是:

  • useState:用于在函数组件中管理状态。
  • useEffect:用于处理副作用,例如数据获取和订阅。

2. 使用 useState 管理状态

2.1 基本用法

useState 是一个函数,接受初始状态作为参数,返回一个数组,数组的第一个元素是当前状态,第二个元素是更新该状态的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { useState } from 'react';

function Counter() {
// 声明一个名为 count 的 state 变量,初始值为 0
const [count, setCount] = useState(0);

return (
<div>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<button onClick={() => setCount(count - 1)}>减少</button>
</div>
);
}

2.2 状态的更新

setCount 函数用于更新 count 的值。当你调用 setCount 时,组件会重新渲染,以显示最新的状态。

2.3 使用函数更新状态

如果新的状态依赖于旧的状态,建议使用函数形式的 setState

1
<button onClick={() => setCount(prevCount => prevCount + 1)}>增加</button>

3. 使用 useEffect 处理副作用

3.1 基本用法

useEffect 用于在组件渲染后执行副作用,比如数据获取、手动 DOM 操作等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { useState, useEffect } from 'react';

function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []); // 空依赖数组表示只在组件挂载时执行一次

if (loading) {
return <div>加载中...</div>;
}

return (
<div>
<h1>获取到的数据:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}

3.2 依赖数组

useEffect 的第二个参数是一个依赖数组,只有当依赖项发生变化时,副作用才会重新执行。如果传入一个空数组 [],则副作用只在组件挂载和卸载时执行。

3.3 清理副作用

如果副作用需要清理(例如,订阅),可以在 useEffect 的返回函数中执行清理逻辑。

1
2
3
4
5
6
7
useEffect(() => {
const subscription = subscribeToSomeData();

return () => {
subscription.unsubscribe(); // 清理逻辑
};
}, []);

4. 综合示例:计数器与数据获取

结合 useStateuseEffect,我们可以创建一个更复杂的组件,既可以计数也能够获取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import React, { useState, useEffect } from 'react';

function App() {
const [count, setCount] = useState(0);
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);

return (
<div>
<h1>计数器</h1>
<p>当前计数:{count}</p>
<button onClick={() => setCount(count + 1)}>增加</button>
<button onClick={() => setCount(count - 1)}>减少</button>

<h1>获取到的数据:</h1>
{loading ? <div>加载中...</div> : <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}

5. 总结

  • useState 允许函数组件管理状态,提供当前状态值和更新函数。
  • useEffect 用于处理副作用,支持依赖管理和清理功能。
  • 使用函数更新状态可以避免闭包问题。

通过掌握 useStateuseEffect,你将能够有效地在 React 函数组件中管理状态和副作用,这是现代 React 开发的重要组成部分。

11 从零到上手学习 React 框架 - 小节提升状态(Lifting State Up)

11 从零到上手学习 React 框架 - 小节提升状态(Lifting State Up)

提升状态(Lifting State Up)是 React 中一个非常重要的概念。在多个组件需要共享某个状态时,React 推荐将该状态提升到它们的最上层父组件。这样,父组件可以将状态传递给子组件,确保它们可以根据这一状态进行渲染和交互。

何时需要提升状态?

当我们发现两个或多个组件需要共享某个状态时,就应该考虑将该状态提升到它们的共同父组件。这种做法可以让状态管理更为集中,同时使数据流更加清晰。以下是一些需要提升状态的例子:

  • 表单元素:多个输入框需要通过一个父组件进行验证和提交。
  • UI 状态:多个组件共享的开关状态(如模态框的打开与关闭)。
  • 过滤条件:在不同组件中根据相同条件过滤数据。

提升状态的步骤

提升状态通常包括以下几个步骤:

  1. 识别需要共享的状态
  2. 将该状态提升到共同父组件
  3. 将状态和状态更新函数传递给子组件
  4. 在子组件中使用props来管理和展示状态

示例代码

下面是一个示例,演示如何提升状态。假设我们有两个组件,TemperatureInputBoilingVerdict,它们都需要共享一个温度状态。

步骤 1: 创建组件

首先,我们创建两个子组件,用于输入温度和展示煮沸判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function TemperatureInput(props) {
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={props.temperature}
onChange={e => props.onTemperatureChange(e.target.value)} />
</fieldset>
);
}

function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}

步骤 2: 创建父组件

接下来,我们创建一个父组件 Calculator,在其中提升状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = { temperature: '' };
}

handleTemperatureChange = (temperature) => {
this.setState({ temperature });
};

render() {
const temperature = this.state.temperature;
return (
<div>
<TemperatureInput
temperature={temperature}
onTemperatureChange={this.handleTemperatureChange} />
<BoilingVerdict celsius={parseFloat(temperature)} />
</div>
);
}
}

代码解析

  • Calculator 组件中,我们使用 this.state 来管理温度状态。
  • handleTemperatureChange 方法更新状态,并被传递给 TemperatureInput 组件。
  • TemperatureInput 组件在输入框发生变化时调用 onTemperatureChange,将输入值传递给父组件。
  • BoilingVerdict 组件通过 props.celsius 接收状态,以判断水是否会煮沸。

小结

提升状态是一种有效的 React 设计模式,确保可以在多个组件之间共享和同步数据。掌握这一模式有助于构建更加清晰和易维护的组件结构,避免不必要的复杂性。

通过以上示例代码和步骤,你应该能够理解并实际操作如何提升状态,来管理多个组件之间的交互。考虑在你的项目中应用这一模式,以简化状态管理和提升组件的可复用性。

12 React 中小节组件之间的通信

12 React 中小节组件之间的通信

在 React 中,组件是构建用户界面的基本单元。有时我们需要在不同组件之间进行数据传递或事件处理。这里将详细介绍小节(子组件)之间的通信方法。

1. 升级与降级数据

在 React 中,数据是通过props(属性)从父组件传递到子组件的。而子组件不能直接更新父组件的状态。如果需要在子组件中处理数据,我们通常使用数据提升的方法,将状态提升到共同的父组件中。

1.1 父组件中的状态

首先,创建一个父组件来管理状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { useState } from 'react';
import ChildA from './ChildA';
import ChildB from './ChildB';

const Parent = () => {
const [data, setData] = useState('');

return (
<div>
<ChildA setData={setData} />
<ChildB data={data} />
</div>
);
};

export default Parent;

1.2 子组件 A(发送数据)

子组件 A 可以通过调用 setData 函数来更新父组件的状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
import React from 'react';

const ChildA = ({ setData }) => {
const handleChange = (e) => {
setData(e.target.value); // 更新父组件的状态
};

return (
<input type="text" onChange={handleChange} placeholder="Type something..." />
);
};

export default ChildA;

1.3 子组件 B(接收数据)

子组件 B 通过 props 接收父组件传来的数据:

1
2
3
4
5
6
7
import React from 'react';

const ChildB = ({ data }) => {
return <div>Received data: {data}</div>; // 显示父组件的状态
};

export default ChildB;

1.4 整体效果

父组件将状态提升到父级,并通过 props 传递给子组件 A 和子组件 B。子组件 A 更新状态,子组件 B 根据变化自动渲染。

2. 通过 Context API 实现组件间通信

在某些情况下,当多个层级的子组件需要共享数据时,使用 Context API 是一个不错的选择。

2.1 创建 Context

首先,创建一个上下文(Context):

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { createContext, useState } from 'react';

export const MyContext = createContext();

export const MyProvider = ({ children }) => {
const [data, setData] = useState('');

return (
<MyContext.Provider value={{ data, setData }}>
{children}
</MyContext.Provider>
);
};

2.2 使用 Context 提供数据

在父组件中使用 MyProvider 来包裹子组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import { MyProvider } from './MyContext';
import ChildA from './ChildA';
import ChildB from './ChildB';

const Parent = () => {
return (
<MyProvider>
<ChildA />
<ChildB />
</MyProvider>
);
};

export default Parent;

2.3 子组件 A(使用 Context)

子组件 A 从上下文中获取 setData 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, { useContext } from 'react';
import { MyContext } from './MyContext';

const ChildA = () => {
const { setData } = useContext(MyContext);

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

return (
<input type="text" onChange={handleChange} placeholder="Type something..." />
);
};

export default ChildA;

2.4 子组件 B(使用 Context)

子组件 B 从上下文中获取 data

1
2
3
4
5
6
7
8
9
10
import React, { useContext } from 'react';
import { MyContext } from './MyContext';

const ChildB = () => {
const { data } = useContext(MyContext);

return <div>Received data: {data}</div>;
};

export default ChildB;

2.5 整体效果

通过 Context API,我们成功地实现了没有直接层级关系的多个组件之间的数据共享。每当子组件 A 更新数据时,子组件 B 都会响应更新。

3. 使用事件机制

另一个组件间通信的方法是通过事件传递。这种方式通常在使用类组件时更加明显,但也可以通过 useEffect 钩子在函数组件中实现。

3.1 使用事件总线

创建一个简单的事件总线:

1
2
3
4
import { EventEmitter } from 'events';

const eventBus = new EventEmitter();
export default eventBus;

3.2 发布事件

在子组件 A 中,通过事件总线发布事件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React from 'react';
import eventBus from './eventBus';

const ChildA = () => {
const handleChange = (e) => {
eventBus.emit('dataChanged', e.target.value); // 发布事件
};

return (
<input type="text" onChange={handleChange} placeholder="Type something..." />
);
};

export default ChildA;

3.3 监听事件

在子组件 B 中,监听这个事件并更新状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import React, { useEffect, useState } from 'react';
import eventBus from './eventBus';

const ChildB = () => {
const [data, setData] = useState('');

useEffect(() => {
const handleDataChange = (newData) => {
setData(newData);
};

eventBus.on('dataChanged', handleDataChange); // 监听事件

return () => {
eventBus.off('dataChanged', handleDataChange); // 清理事件监听
};
}, []);

return <div>Received data: {data}</div>;
};

export default ChildB;

3.4 整体效果

通过事件机制,我们能够实现子组件之间的通信,尽管它们没有直接的层级关系。子组件 A 发布事件,子组件 B 监听并响应这个事件。

总结

在 React 中,小节组件之间的通信可以通过以下几种方式进行:

  1. 数提升:将组件的状态提升到共同的父组件中,然后通过 props 方式传递。
  2. Context API:适合深层嵌套的组件间数据传递。
  3. 事件机制:使用事件总线来实现非关联组件之间的通信。

选择合适的通信方式可以有效提升应用的结构和可维护性。