1、全局安装create-react-app框架
npm install -g create-react-app
2、创建项目your-app
create-react-app your-app
这需要等待一段时间,这个过程实际上会安装三个东西
- react: react的顶级库
- react-dom: 因为react有很多的运行环境,比如app端的react-native, 我们要在web上运行就使用 react-dom
- react-scripts: 包含运行和打包react应用程序的所有脚本及配置
3、组件的书写
在 React 中,组件是构建用户界面的基本单元,可以分为两种主要类型:类组件和函数组件。除了这两种主要的组件类型,React 还引入了无状态组件和高阶组件等概念,以提供更多的组件抽象和复用方式。在现代 React 开发中,随着 React Hooks 的引入,函数组件变得更加强大,可以在函数组件中使用状态和其他 React 特性,因此在许多场景下函数组件是首选。
3.1、类组件(Class Components):
- 类组件是使用 ES6 类语法定义的 React 组件。
- 类组件继承自
React.Component
类,具有状态(state)和生命周期方法。 - 类组件通过
render
方法返回用于渲染 UI 的 React 元素
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
// 初始状态
};
}
render() {
return (
// JSX 表达式,描述组件的 UI 结构
);
}
}
3.2、函数组件(Functional Components):
- 函数组件是使用函数语法定义的 React 组件。
- 函数组件没有内部状态(state)或生命周期方法,但可以使用 React Hooks 来引入状态和其他特性。
- 函数组件通过函数的返回值描述 UI。
import React from 'react';
function MyComponent(props) {
// 使用 props 对象传递属性
return (
// JSX 表达式,描述组件的 UI 结构
);
}
3.3、无状态组件
当说“无状态组件”时,通常指的是函数式组件,它不维护状态(state),也没有生命周期方法。下面是一个简单的无状态组件示例:
import React from 'react';
function StatelessComponent(props) {
return (
<div>
<h1>Hello, {props.name}!</h1>
<p>This is a stateless component.</p>
</div>
);
}
3.4、高阶组件
一般用于路由拦截
而高阶组件(Higher-Order Component,HOC)是一个函数,接收一个组件作为参数并返回一个新的组件。高阶组件本身不是组件,而是一个工厂函数,用于增强现有组件的功能。下面是一个简单的高阶组件示例:
import React from 'react';
function withLogger(Component) {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${Component.name} is mounted.`);
}
render() {
return <Component {...this.props} />;
}
};
}
// 使用高阶组件增强现有组件
const EnhancedComponent = withLogger(StatelessComponent);
// 在其他地方使用增强后的组件
function App() {
return <EnhancedComponent name="Alice" />;
}
3.5、函数组件与类组件实例
import React, { Component } from 'react';
function App() {
return (
<div className="App">
<h1> APPvue</h1>
<ClassTest></ClassTest>
</div>
);
}
export default App;
class Test extends Component {
var bg = {
background: "red",
};
render() {
return (
<h1>
<p className="active">hello world</p>
<p style={this.bg}>你好啊</p>
<label htmlFor="testID">名字:</label>
<input type="text" name="li" id="testID" />
</h1>
);
}
}
export default Test;
4、JSX语法
在 React 中,JSX(JavaScript XML)是一种语法扩展,使得可以在 JavaScript 中书写类似 XML/HTML 的代码,而不必使用 React.createElement
这样的方法。当 JSX 代码在 React 应用中被渲染时,它最终会被转化为 JavaScript 对象,然后通过 React 的虚拟 DOM 机制进行处理和更新
4.1、JSX 代码:
const element = <div className="my-class">Hello, React!</div>;
4.2、Babel 转译:
JSX 代码会经过 Babel 或其他类似的工具进行转译。Babel 将 JSX 转译为对 React.createElement
方法的调用,这个方法用于创建虚拟 DOM 元素。在这个步骤中,JSX 元素被转译为对 React.createElement
的调用,该调用返回一个描述元素的 JavaScript 对象。
const element = React.createElement('div', { className: 'my-class' },
'Hello, React!');
4.3、 创建虚拟 DOM 元素:
React.createElement
返回的对象是一个描述虚拟 DOM 元素的 JavaScript 对象。这个对象包含了元素的类型、属性、子元素等信息。
const elementObject = {
type: 'div',
props: {
className: 'my-class',
children: 'Hello, React!',
},
};
4.4、 虚拟 DOM 渲染:
将虚拟 DOM 元素传递给 React 渲染引擎。React 使用虚拟 DOM 对象构建实际的 DOM 元素,并将其插入到页面中。
最终,通过这个过程,JSX 代码被转化为了在页面上显示的 HTML 元素。这种虚拟 DOM 的机制使得 React 能够高效地更新页面,并减少了直接操作 DOM 的开销。
5、react绑定事件方式
5.1、在 JSX 中直接绑定事件处理函数:
- 在 JSX 中直接通过
{}
中插入事件处理函数。如点击添加3
class MyComponent extends React.Component {
handleClick() {
console.log('Button clicked!');
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
5.2、使用箭头函数绑定事件处理函数:
- 使用箭头函数可以确保在事件处理函数中
this
的指向是当前组件实例。如点击添加3
class MyComponent extends React.Component {
handleClick = () => {
console.log('Button clicked!');
};
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
5.3、在构造函数中绑定事件处理函数:
- 在构造函数中通过
bind
方法显式绑定事件处理函数。(不推荐)
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Button clicked!');
}
render() {
return (
<button onClick={this.handleClick}>Click me</button>
);
}
}
5.4、使用内联箭头函数:
- 在事件处理函数内联使用箭头函数。如点击添加1与点击添加4
class MyComponent extends React.Component {
render() {
return (
<button onClick={() => this.handleClick()}>Click me</button>
);
}
}
5.5、实例
如点击添加2的this是undefined,其余是实例本身
<button type="button" onClick={() => { console.log("111"); }} >
点击添加1
</button>
<button type="button" onClick={this.click2.bind(this)}>
点击添加2
</button>
<button type="button" onClick={this.click3}>
点击添加3
</button>
<button type="button" onClick={() => { this.click4(); }}>
点击添加4
</button>
</h1>
click2() {
console.log("222");
}
click3 = (evt) => {
console.log("33");
console.log(evt.target);
};
click4 = () => {
console.log("44");
};
}
5.6、事件系统机制
-
事件代理:
- React 采用了事件代理(event delegation)的机制。这意味着事件不是直接绑定到每个元素上,而是绑定到它们的共同父元素上,通常是组件的根节点。这样一来,只需要在一个地方监听事件,而不是为每个可能触发事件的元素都添加监听器。
- 通过事件冒泡的方式,React 可以在根节点上捕获所有事件,然后通过组件的虚拟 DOM 树找到真正触发事件的元素。
-
不用解绑:
- 由于事件是在父元素上进行监听,而不是直接在每个子元素上,所以不需要手动解绑事件。在组件卸载时,React 会自动处理事件监听器的清理工作,确保没有发生内存泄漏。
- Event 对象:
import React from 'react';
class MyComponent extends React.Component {
handleClickWithParams = (param, event) => {
// 在事件处理函数中使用 param 和 event
console.log('Param:', param);
console.log('Event:', event);
};
render() {
const paramValue = 'Hello, React!';
return (
<button onClick={(e) => this.handleClickWithParams(paramValue, e)}>
Click me
</button>
);
}
}
export default MyComponent;
6、改变this指向
7、组件的样式
- 使用 class
先引入样式表import './MyComponent.css'; 然后写类名
8、Ref的应用
9、组件的数据挂载方式
9.1、类组件state
this.state 是纯js对象,在vue中,data属性是利用 Object.defineProperty 处理过的,更改data的 数据的时候会触发数据的 getter 和 setter ,但是React中没有做这样的处理,如果直接更改的话, react是无法得知的,所以,需要使用特殊的更改状态的方法 setState 。react是手动更新。
9.2、类组件this.setState
在 React 中,this.setState
方法有两种使用方式:
9.2.1、对象形式:
当状态更新不依赖于当前状态时,可以传递一个更新的状态对象。
this.setState(newStateObject, callback)
,其中 newStateObject
是一个包含要更新的状态的对象,callback
是一个可选的回调函数,它将在状态更新完成并且组件重新渲染后被调用。
this.setState({ count: this.state.count + 1 }, () => {
console.log('State updated:', this.state.count);
});
9.2.2、函数形式:
当状态更新依赖于当前状态时,可以传递一个更新状态的函数,该函数接收先前的状态作为参数。
this.setState(updateStateFunction, callback)
,其中 updateStateFunction
是一个函数,接收先前的状态作为参数,并返回一个包含要更新的部分状态的对象,callback
是一个可选的回调函数。
this.setState((prevState,props) => {
return { count: prevState.count + 1 };
}, () => {
console.log('State updated:', this.state.count);
});
9.3、类组件实列
返回遍历列表,交互后更新数据,然后更新页面
拓展axios请求数据,axios.get("url",{query:{key:value}}或者
axios.get({
url:"url",
header:{key:value},
query:{key:value},
}
最后筛选列表,搜索时通过filter筛选关键字
import React, { Component } from "react";
export default class TestProps extends Component {
state = {
people: ["lili", "lier", "lisan"],
};
myref = React.createRef();
render() {
return (
<>
<input type="text" ref={this.myref} />
<button
onClick={() => {
this.click1();
}}
>
增加人们
</button>
<br />
{this.show()}
</>
);
}
show = () => {
return this.state.people.map((item, index) => {
return (
<h1 key={index}>
{item}--{index}
<button
onClick={() => {
this.click2(index);
}}
>
删除
</button>
</h1>
);
});
};
click1 = () => {
// console.log(this.myref.current.value);
let temp = [...this.state.people, this.myref.current.value];
this.setState({
people: temp,
});
this.myref.current.value = "";
};
click2 = (index) => {
console.log(index);
let temp = this.state.people.slice();
temp.splice(index, 1);
this.setState({
people: temp,
});
};
}
9.4、函数组件的数据维护
在 React 中,函数组件内部没有像类组件那样的实例对象,因此没有 this
对象。因此,不能像在类组件中那样使用 this.setState
来修改状态。在函数组件中,你可以使用 useState
钩子来创建和更新状态。
import React, { useState } from 'react';
function Counter() {
// 使用 useState 创建状态变量 count 和对应的更新函数 setCount,初始值为 0
const [count, setCount] = useState(0);
// 定义一个处理点击事件的函数,每次点击时更新 count 状态
const handleClick = () => {
// 使用 setCount 更新状态
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
{/* 绑定点击事件 */}
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default Counter;
10、props 父子通讯与 propTypes的使用
10.1、父传递数据给子组件
1、安装 prop-types 库:npm install prop-types
2、在组件中定义 propTypes: 在组件文件中,通过引入 PropTypes
并将其附加到组件类的 propTypes
静态属性上,定义组件接收的属性及其类型。
import React from 'react';
import PropTypes from 'prop-types';
class MyComponent extends React.Component {
// 定义 propTypes
static defaultProps = {
myname:"默认的myname"
}
static propTypes = {
name: PropTypes.string.isRequired, // 字符串类型,且是必需的
age: PropTypes.number, // 数字类型
isStudent: PropTypes.bool.isRequired, // 布尔类型,且是必需的
callback: PropTypes.func, // 函数类型
items: PropTypes.arrayOf(PropTypes.string), // 字符串数组
};
render() {
// 组件渲染...
}
}
export default MyComponent;
3、在组件使用时传递属性: 在使用组件时,确保传递的属性符合在 propTypes
中定义的规范。
import React from 'react';
import MyComponent from './MyComponent';
function App() {
return (
<MyComponent
name="John"
age={25}
isStudent={true}
callback={() => console.log('Callback')}
items={['item1', 'item2']}
/>
);
}
10.2、子传递数据给父组件
通过在父组件中定义一个回调函数,然后将该回调函数作为 prop 传递给子组件。子组件在适当的时机调用这个回调函数,并传递数据作为参数。
类组件
import React, { Component } from 'react';
// 子组件
class ChildComponent extends Component {
handleClick = () => {
// 子组件调用父组件传递过来的回调函数,并传递数据
this.props.onChildClick('Data from child');
};
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
// 父组件
class ParentComponent extends Component {
handleChildClick = (dataFromChild) => {
console.log('Received data from child:', dataFromChild);
// 在这里处理从子组件传递过来的数据
};
render() {
return (
<div>
<h1>Parent Component</h1>
{/* 将回调函数作为 prop 传递给子组件 */}
<ChildComponent onChildClick={this.handleChildClick} />
</div>
);
}
}
export default ParentComponent;
函数组件
import React, { useState } from 'react';
// 子组件
function ChildComponent({ onChildClick }) {
const handleClick = () => {
// 子组件调用父组件传递过来的回调函数,并传递数据
onChildClick('Data from child');
};
return <button onClick={handleClick}>Click Me</button>;
}
// 父组件
function ParentComponent() {
const handleChildClick = (dataFromChild) => {
console.log('Received data from child:', dataFromChild);
// 在这里处理从子组件传递过来的数据
};
return (
<div>
<h1>Parent Component</h1>
{/* 将回调函数作为 prop 传递给子组件 */}
<ChildComponent onChildClick={handleChildClick} />
</div>
);
}
export default ParentComponent;
11、受控组件与非受控组件
React 中的表单元素可以分为两种主要类型:受控组件和非受控组件
11.1、受控组件(Controlled Components):
受控组件是指表单元素的值(如 <input>
、<textarea>
、<select>
)由 React 的状态(state)控制的组件。当用户输入时,React 组件的状态会更新,从而更新表单元素的值。受控组件通过事件处理函数来处理用户输入,并通过 setState
更新组件的状态。onInput onChange
类组件的受控组件
import React, { Component } from 'react';
class ControlledComponentClass extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: '',
};
}
handleChange = (event) => {
this.setState({ inputValue: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
console.log('Submitted value:', this.state.inputValue);
// 可以在这里进行进一步处理,如提交到服务器等
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Enter something:
<input
type="text"
value={this.state.inputValue}
onChange={this.handleChange}
/>
</label>
<button type="submit">Submit</button>
</form>
);
}
}
export default ControlledComponentClass;
函数组件的受控组件
import React, { useState } from 'react';
function ControlledComponent() {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
};
return (
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
);
}
11.2、非受控组件(Uncontrolled Components):
非受控组件是指表单元素的值不受 React 状态的控制。在非受控组件中,表单元素的值由 DOM 自身管理。开发者可以通过 ref
来获取表单元素的值。
import React, { useRef } from 'react';
function UncontrolledComponent() {
const inputRef = useRef();
const handleClick = () => {
console.log('Input Value:', inputRef.current.value);
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Get Value</button>
</div>
);
}
控制组件是否展示: {this.state.show && <Nav name="控制组件是否展示"></Nav>}
11.3、具体实例
也可以通过ref调用子组件。注意:父组件不要直接修改子组件的数据
import React, { Component } from "react";
class Field extends Component {
render() {
return (
<>
<div>
<label>{this.props.label}:</label>
<input
type={this.props.type}
value={this.props.username}
onChange={(evt) => {
this.props.event(evt);
}}
/>
</div>
</>
);
}
}
export default class App extends Component {
state = {
username: "use",
password: "",
};
handelChange(e) {
this.setState({
inpValu: e.target.value,
});
}
render() {
return (
<>
<Field
label="用户名"
type="text"
event={(evt) => {
this.setState({ user: evt.target.value });
}}
value={this.state.username}
></Field>
<Field
label="密码"
type="password"
event={(evt) => {
this.setState({ password: evt.target.value });
}}
value={this.state.password}
></Field>
<button onClick={console.log(this.state.user, this.state.password)}>
登录
</button>
<button
onClick={() => {
this.setState({ user: "", password: "" });
}}
>
重置
</button>
</>
);
}
}
12、兄弟传递数据
12.1、利用了发布订阅模式
但在实际项目中,确保合理使用并在不需要时取消订阅是很重要的,以避免潜在的内存泄漏问题。
import React, { useState, useEffect } from 'react';
// 创建一个全局的发布-订阅对象
const eventEmitter = {
subscribers: {},
subscribe: function (event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = [];
}
this.subscribers[event].push(callback);
return () => {
this.subscribers[event] = this.subscribers[event].filter(subscriber => subscriber !== callback);
};
},
publish: function (event, data) {
if (this.subscribers[event]) {
this.subscribers[event].forEach(subscriber => subscriber(data));
}
},
};
// 订阅者组件
function SubscriberComponent() {
const [message, setMessage] = useState('');
useEffect(() => {
// 订阅事件,并返回一个取消订阅的函数
const unsubscribe = eventEmitter.subscribe('customEvent', (data) => {
setMessage(data);
});
// 在组件卸载时取消订阅,以防止内存泄漏
return () => {
unsubscribe();
};
}, []);
return (
<div>
<h2>Subscriber Component</h2>
<p>Received message: {message}</p>
</div>
);
}
// 发布者组件
function PublisherComponent() {
const handleClick = () => {
// 发布事件,通知所有订阅者
eventEmitter.publish('customEvent', 'Hello from Publisher!');
};
return (
<div>
<h2>Publisher Component</h2>
<button onClick={handleClick}>Publish Message</button>
</div>
);
}
// 主应用组件
function App() {
return (
<div>
<PublisherComponent />
<SubscriberComponent />
</div>
);
}
export default App;
12.2、GlobalContext
类组件
import React, { Component } from "react";
// 创建上下文
const Global = React.createContext();
export default class App extends Component {
state = {
name: "lili",
age: 18,
};
render() {
return (
<Global.Provider
value={{
name: this.state.name,
age: this.state.age,
changeAge: (value) => {
this.setState({
age: this.state.age + value,
});
},
}}
>
<>
<div>创建上下文--创建</div>
<Decriber></Decriber>
</>
</Global.Provider>
);
}
}
class Decriber extends Component {
render() {
return (
<Global.Consumer>
{(value) => (
<>
<h1>消费者以回调函数形式获取数据</h1>
<h1>
{value.name}
{value.age}
</h1>
<button
onClick={() => {
value.changeAge(2);
}}
>
修改年龄+2
</button>
</>
)}
</Global.Consumer>
);
}
}
函数组件
import React, { createContext, useContext } from 'react';
const SharedDataContext = createContext();
function ParentComponent() {
const sharedData = 'Hello from Parent!';
return (
<SharedDataContext.Provider value={sharedData}>
<div>
<ChildComponent1 />
<ChildComponent2 />
</div>
</SharedDataContext.Provider>
);
}
function ChildComponent1() {
const data = useContext(SharedDataContext);
return <p>Child 1: {data}</p>;
}
function ChildComponent2() {
const data = useContext(SharedDataContext);
return <p>Child 2: {data}</p>;
}
12.3、插槽实现父子通信--组件复用
import React, { Component } from "react";
// 插槽
const Global = React.createContext();
export default class App extends Component {
state = {
show: true,
};
render() {
return (
<>
<div>插槽-减少部分通信</div>
{this.state.show && (
<Decriber>
<div>我是插槽部分的内容</div>
<button
onClick={() => {
this.setState({ show: false });
}}
>
我是隐藏子组件
</button>
</Decriber>
)}
</>
);
}
}
class Decriber extends Component {
render() {
return (
<>
<h1>插槽</h1>
{this.props.children}
</>
);
}
}
12.4、使用第三方状态管理库:
- 对于更大型的应用程序,可能需要考虑使用像 Redux、MobX 等状态管理库,以便更灵活地管理和共享状态。
- 具体使用参考:redux的使用
13、生命周期
13.1、类组件的生命周期
老版本执行顺序图谱
import React, { Component } from "react";
// 插槽
const Global = React.createContext();
export default class App extends Component {
state = {
show: true,
};
componentWillMount() {
console.log("componentWillMount");
}
componentWillUnmount() {
console.log("componentWillUnmount");
window.onresize = null;
}
componentDidMount() {
// 数据请求,订阅信息,初始化DOM,Betterscroll
console.log("componentDidMount");
window.onresize = () => {
console.log("onresize");
};
}
componentDidUpdate(prevProps, prevState, value) {
console.log("componentDidUpdate", value);
}
componentWillUpdate() {
console.log("componentWillUpdate");
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate", nextProps, nextState);
// return true;
return JSON.stringify(this.state) !== JSON.stringify(nextState);
// 看需求
}
// static getDerivedStateFromProps() {
// console.log("getDerivedStateFromProps");
// return this.setState({ show: 111 });
// }
getSnapshotBeforeUpdate() {
console.log("getSnapshotBeforeUpdate");
return 100;
}
render() {
console.log("render");
return (
<>
<div>插槽-减少部分通信</div>
<div
onClick={() => {
this.setState({ show: false });
}}
>
改变
</div>
<Decriber name={this.state.show}></Decriber>
</>
);
}
}
class Decriber extends Component {
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps");
console.log("componentWillReceiveProps", this.props.show, nextProps);
}
render() {
console.log("33");
return (
<>
<h1>插槽</h1>
{this.props.children}
</>
);
}
}
17版本生命周期
新增了两个生命周期函数:
static getDerivedStateFromProps(nextProps, prevState)
在render
前调用,在初始挂载以及后续更新时都会被调用。他应该返回一个对象来更新state
。如果返回null
则不更新任何内容。它接收两个参数,一个是传进来的nextProps
和之前的prevState
。getSnapshotBeforeUpdate(prevProps, prevState)
在更新阶段 render 后挂载到真实 DOM 前进行的操作,它使得组件能在发生更改之前从 DOM 中捕获一些信息。此组件返回的任何值将作为componentDidUpdate
的第三个参数
static getDerivedStateFromProps(nextProps, prevState){
console.log('getDerivedStateFromProps',nextProps,prevState);
return null;
}
getSnapshotBeforeUpdate(prevProps, prevState){
return "getSnapshotBeforeUpdate";
}
// 组件更新成功钩子
componentDidUpdate(prevProps, prevState, snapshot) {
console.log(snapshot); // "getSnapshotBeforeUpdate"
}
3.删除了以下生命周期函数:
componentWillMount
componentWillReceiveProps
componentWillUpdate
4.从这个版本开始,只有新的 UNSAFE_
生命周期名称将起作用:
UNSAFE_componentWillMount
UNSAFE_componentWillReceiveProps
UNSAFE_componentWillUpdate
13.2、函数组件的仿生命周期
(1)回顾下在 Class Component
的数据请求:
- 在
componentDidMount
初始化发请求; - 在
componentDidUpdate
判断参数是否变化,变化就调用请求函数重新请求数据; - 在
componentWillUnmount
生命周期取消发送请求。
(2)那么在函数组件中我们该如何做呢?答案是 useEffect
。
useEffect
接收一个回调函数作为其第一个参数,该回调函数包含在组件挂载后或依赖项发生变化时执行的副作用代码。useEffect
的第二个参数是依赖数组,如果依赖数组中的值发生变化,就会触发useEffect
的执行。如果依赖数组为空,useEffect
仅在组件挂载和卸载时执行。- 在回调函数内部,可以执行各种副作用,比如数据获取、订阅、手动操作 DOM 等。
- 如果需要在组件卸载前执行一些清理操作,可以在回调函数内返回一个清理函数。
1、useEffect
就是一个 Effect Hook
,给函数组件增加了操作副作用的能力。useEffect
的基本用法是在组件渲染完成后执行第一个参数函数,完成一些操作。类似于在 class 组件中,我们去监听原生 DOM 事件时,会在 componentDidMount
这个生命周期中去做,因为在这里可以获取到已经挂载的真实 DOM。
2、我们也会在组件卸载的时候去取消事件监听避免内存泄露。useEffect第一个参数返回的回调函数就是清理副作用,相当于卸载函数。useEffect
清理规则是:
- 首次渲染不会进行清理,会在下一次渲染,清除上一次的副作用;
- 卸载阶段也会执行清除操作。
useEffect(() => {
function handleClick(status) {
document.title = `You clicked ${count} times`;
}
document.body.addEventListener("click",handleClick,false);
return function cleanup() {
document.body.removeEventListener("click",handleClick,false);
};
});
function Parent(){
const [query,setQuery] = useState('q');
const fetchData = useCallback(()=>{
...省略函数体的具体实现
},[query]);
return <Child fetchData={fetchData} />
}
function Child({fetchData}){
const [data,setData] = useState(null);
useEffect(()=>{
fetchData().then(setData);
},[fetchData])
}
(3)注意:
无限循环调用:
- 如果在
useEffect
的回调函数内部触发了导致组件状态变化的操作,并且这个操作引起了useEffect
的再次调用,就会导致无限循环调用。
function ExampleComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); // 这会导致无限循环调用
}, [count]);
return (
<div>
<p>Count: {count}</p>
</div>
);
}
循环或条件语句中调用 useEffect
- 使用
useEffect
时,需要注意不要在循环或条件语句中调用它,以确保它在每次渲染时都能被正确执行。
异步操作:
useEffect
内部不支持直接的异步函数。如果需要在useEffect
中执行异步操作,可以在内部定义一个异步函数,并立即执行它。
下面是一个示例,展示了在 useEffect
内部直接使用异步函数的错误方式:
import React, { useEffect, useState } from 'react';
function ExampleComponent() {
const [data, setData] = useState(null);
useEffect(async () => {
// 错误的例子:在 useEffect 内部使用异步函数
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
}, []);
return (
<div>
<p>Data: {data}</p>
</div>
);
}
export default ExampleComponent;
正确的方式是在 useEffect
内部创建一个普通的函数,并在其中执行异步操作:
import React, { useEffect, useState } from 'react';
function ExampleComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// 正确的例子:在 useEffect 内部执行异步操作
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []);
return (
<div>
<p>Data: {data}</p>
</div>
);
}
export default ExampleComponent;
依赖项的正确使用:
- 在
useEffect
的依赖项数组中,确保包含了函数内部使用的所有变量。如果依赖项未正确设置,可能导致副作用不按预期执行。在依赖数组中添加依赖项时,需要确保这些依赖项是稳定的,避免不必要的副作用触发。
清理操作:
- 如果
useEffect
返回一个函数,该函数将在组件卸载前执行,可以用于清理操作,比如取消订阅、清除计时器等。