react 笔记
React 用于构建用户界面的 js库。使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作“组件”。
class组件
通过 class 定义组件,继承React.Component,定义render方法使用jsx语法书写结构,返回一个React元素。
访问属性: this.props
添加点击事件:onClick={fn}
定义状态:在constructor 初始化 this.state
修改状态:this.setState,只会指定属性的值。
多组件共享状态:在父组件中定义状态,然后通过props传递给子组件。click函数同样。
不可变性:不直接修改数据有利于追踪数据变化,实现回退功能。确定组件渲染时间。
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
27class ShoppingList extends React.Component { constructor(props) { super(props); this.state = { value: null, }; } render() { return ( <div className="shopping-list"> <h1>Shopping List for {this.props.name}</h1> <button className="square" onClick={() => this.setState({value: 'X'})}> {this.state.value} </button> <ul> <li>Instagram</li> <li>WhatsApp</li> <li>Oculus</li> </ul> </div> ); } }
函数组件
接受props属性,返回一个React元素
访问属性: props
1
2
3
4
5
6
7
8function Square(props) { return ( <button className="square" onClick={props.onClick}> {props.value} </button> ); }
key的作用
列表渲染时,判断如果现在存在,但之前不存在这个key会创建这个元素;如果现在不存在,但之前存在会删除这个元素。如果key发生了变化。会删除之前的然后新创建一个。
数组的索引不适合作为key。排序、新增、删除操作时,数组的索引会变动。导致key变动,导致渲染问题。
JSX语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
211. 将变量用大括号包裹。 const element = <h1>Hello, {name}</h1>; 2. 执行函数表达式 function formatName(user) { return user.firstName + ' ' + user.lastName; } const element = <h1>Hello, {formatName(user)}</h1>; 3. 访问属性 const element = <img src={user.avatarUrl}></img>; 4. 添加属性名,采用驼峰命名 const element = <h1 className="hell">Hello</h1>; 5. 闭合标签 /> const element = <img src={user.avatarUrl} />; 6. 防止XSS注入攻击,title在被渲染之前会被自动转义。 const title = response.potentiallyMaliciousInput; const element = <h1>{title}</h1>; 8.
元素渲染
1
2
3
4
5
6
7
8
9
10
111. 这种我们称之为元素,是一个普通对象。是不可变的。跟浏览器的元素不一样。需要执行ReactDOM.render渲染 const element = <h1>Hello, world</h1>; ReactDOM.render(element, document.getElementById('root')); 2. 元素和组件不一样。 3. 自定义组件也可以做元素。属性会被转为对象传递给组件。 const element = <Welcome name="Sara" />; 4. 元素渲染流程 首先通过ReacDOM.render渲染元素element 。然后调用Welcome组件,传入属性。Welcome组件返回render内容。React DOM更新内容。 5.
组件
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
2731. 组件名必须大写。 2. 提取组件。 function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } 3. 组合组件。 function App() { return ( <div> <Welcome name="Sara" /> <Welcome name="Cahal" /> <Welcome name="Edite" /> </div> ); } 4. props只读。 不能修改自身的 props 5. 通过State让组件自我更新。 在class组件的constructor中初始化state。在render中调用。在componentDidMount中添加定时器修改。在componentWillUnmount中删除定时器。 调用顺序:首先React 调用 Clock组件的构造函数。初始化state。调用组件的render。更新DOM。执行生命周期函数ComponentDidMount,设置计时器。浏览器执行定时器。调用setState。React得知state变化。重新调用render。一旦Clock组件移除。执行生命周期函数componentWillUnmount。 class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } 6. state 不可直接修改。只能通过setState调用。 State 的更新可能是异步的。 setState不能依赖state修改 this.setState({ counter: this.state.counter + this.props.increment, }); 可以通过函数 // Correct this.setState((state, props) => ({ counter: state.counter + props.increment })); State 的更新会被合并,合并是浅合并,不会修改其他state this.setState({ posts: response.posts }); this.setState({ comments: response.comments }); 7. 数据自上向下流动。 8. 事件处理 驼峰命名 <button onClick={activateLasers}>Activate Lasers</button> 不能通过返回 false 的方式阻止默认行为 在class组件中使用,需要在constructor中绑定this或者使用箭头函数。onClick={() => this.handleClick()} class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // 为了在回调中使用 `this`,这个绑定是必不可少的 this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } } ReactDOM.render( <Toggle />, document.getElementById('root') ); 传递参数 <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button> 9. 条件渲染。 通过if function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } 通过与运算符 && isLoggedIn 为true才会渲染。为false会被忽略。 function Greeting(props) { const isLoggedIn = props.isLoggedIn; return isLoggedIn && <UserGreeting />; } 三目运算符 render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div> ); } 阻止条件渲染 render 方法直接返回 null 10. 列表渲染 function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') ); 内联map function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); } 11. 表单 受控组件:state作为数据源。通过setState更新。 同类标签input、textarea、select <input type="text" value={this.state.value} onChange={this.handleChange} /> handleChange(event) { this.setState({value: event.target.value}); } 非受控组件 真实数据储存在 DOM 节点 <input type="text" ref={this.input} /> alert('A name was submitted: ' + this.input.current.value); Formik import React from 'react'; import {useFormik } from 'formik'; const SignupForm = () => { // Note that we have to initialize ALL of fields with values. These // could come from props, but since we don’t want to prefill this form, // we just use an empty string. If we don’t do this, React will yell // at us. const formik = useFormik({ initialValues: { firstName: '', lastName: '', email: '', }, onSubmit: values => { alert(JSON.stringify(values, null, 2)); }, }); return ( <form onSubmit={formik.handleSubmit}> <label htmlFor="firstName">First Name</label> <input id="firstName" name="firstName" type="text" onChange={formik.handleChange} value={formik.values.firstName} /> <label htmlFor="lastName">Last Name</label> <input id="lastName" name="lastName" type="text" onChange={formik.handleChange} value={formik.values.lastName} /> <label htmlFor="email">Email Address</label> <input id="email" name="email" type="email" onChange={formik.handleChange} value={formik.values.email} /> <button type="submit">Submit</button> </form> ); }; 12. 组合与继承 使用children,子节点内容由父节点确定 function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); } 通过属性left、right传递不同的组件。 function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); } 如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。 13. React开发步骤 首先根据设计UI划分组件层级(根据单一功能原则把组件当做一个函数或对象,如果它负责更多的功能,应该拆分成更小的组件); 然后用React 创建一个静态版本(自上而下比较方便,但自下而上适合大型项目同时编写测试更简单) 然后确定需要用到的最小且完整的state 然后确定state位置(所有需要用到该state的共同父级,如果没有就创建一个)。 最后添加反向数据流(较低层组件通过事件更新较高层级组件的state)
高级功能
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
3991. 代码分割 避免代码体积过大导致加载时间过长;避免加载用户不需要的代码。 动态 import() import { add } from './math'; console.log(add(16, 26)); 例如 import("./math").then(math => { console.log(math.add(16, 26)); }); React.lazy import OtherComponent from './OtherComponent'; 例如 const OtherComponent = React.lazy(() => import('./OtherComponent')); 然后应在 Suspense 组件中渲染 lazy 组件 import React, { Suspense } from 'react'; const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> </div> ); } 基于路由的代码分割 import React, { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> ); React.lazy 目前只支持默认导出(default exports) 2. Context Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法 适合管理locale,theme,或者一些缓存数据 通过React.createContext创建context对象 通过ThemeContext.Provider并设置value向下传递context内容 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。 ** 设置MyClass.contextType = MyContext; class组件可以直接通过 this.context消费context ** 设置MyContext.Consumer消费 <MyContext.Consumer> {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer> 不利于组件的复用(替代方案使用组件组合,将底层组件整个传递下去,但会使组件更复杂) const ThemeContext = React.createContext('light'); class App extends React.Component { render() { // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。 // 无论多深,任何组件都能读取这个值。 // 在这个例子中,我们将 “dark” 作为当前的值传递下去。 return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 动态 Context 将context内容设置为一个对象。 theme-context.js export const themes = { light: { foreground: '#000000', background: '#eeeeee', }, dark: { foreground: '#ffffff', background: '#222222', }, }; export const ThemeContext = React.createContext( themes.dark // 默认值 ); themed-button.js import {ThemeContext} from './theme-context'; class ThemedButton extends React.Component { render() { let props = this.props; let theme = this.context; return ( <button {...props} style={{backgroundColor: theme.background}} /> ); } } ThemedButton.contextType = ThemeContext; export default ThemedButton; app.js import {ThemeContext, themes} from './theme-context'; import ThemeTogglerButton from './theme-toggler-button'; class App extends React.Component { constructor(props) { super(props); this.toggleTheme = () => { this.setState(state => ({ theme: state.theme === themes.dark ? themes.light : themes.dark, })); }; // State 也包含了更新函数,因此它会被传递进 context provider。 this.state = { theme: themes.light, toggleTheme: this.toggleTheme, }; } render() { // 整个 state 都被传递进 provider return ( <ThemeContext.Provider value={this.state}> <Content /> </ThemeContext.Provider> ); } } function Content() { return ( <div> <ThemeTogglerButton /> </div> ); } ReactDOM.render(<App />, document.root); 消费多个 Context // 一个组件可能会消费多个 context function Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext.Consumer> )} </ThemeContext.Consumer> ); } 3. 错误边界 错误边界是一个React 组件,捕获子组件树上的js错误,并渲染备用UI 实现方式,在class组件上定义 static getDerivedStateFromError或componentDidCatch方法 class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // 更新 state 使下一次渲染能够显示降级后的 UI return { hasError: true }; } componentDidCatch(error, errorInfo) { // 你同样可以将错误日志上报给服务器 logErrorToMyService(error, errorInfo); } render() { if (this.state.hasError) { // 你可以自定义降级后的 UI 并渲染 return <h1>Something went wrong.</h1>; } return this.props.children; } } <ErrorBoundary> <MyWidget /> </ErrorBoundary> 4. Refs 转发 将ref通过组件传递到子组件,主要解决类似FancyButton 这样封装单个控件的高可复用组件可以直接对button进行操作。一般不推荐使用。 React.createRef()创建ref;React.forwardRef接收ref const FancyButton = React.forwardRef((props, ref) => ( <button ref={ref} className="FancyButton"> {props.children} </button> )); // 你可以直接获取 DOM button 的 ref: const ref = React.createRef(); <FancyButton ref={ref}>Click me!</FancyButton>; 5. Fragments 一个组件返回多个元素 无需向 DOM 添加额外节点 render() { return ( <React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> ); } 短语法 class Columns extends React.Component { render() { return ( <> <td>Hello</td> <td>World</td> </> ); } } 6. 高阶组件 高阶组件是参数为组件,返回值为新组件的函数 组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件 使用 HOC 解决横切关注点问题 不要改变原始组件。 不要将不相关的 props 传递给被包裹的组件 包装显示名称以便轻松调试 不要在 render 方法中使用 HOC,这将导致子树每次渲染都会进行卸载,和重新挂载的操作! // 此函数接收一个组件... function withSubscription(WrappedComponent, selectData) { // ...并返回另一个组件... return class extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state = { data: selectData(DataSource, props) }; } componentDidMount() { // ...负责订阅相关的操作... DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { DataSource.removeChangeListener(this.handleChange); } handleChange() { this.setState({ data: selectData(DataSource, this.props) }); } render() { // ... 并使用新数据渲染被包装的组件! // 请注意,我们可能还会传递其他属性 return <WrappedComponent data={this.state.data} {...this.props} />; } }; } const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments() ); 7. 深入JSX 在运行时选择组件类型 import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // 正确!JSX 类型可以是大写字母开头的变量。 const SpecificStory = components[props.storyType]; return <SpecificStory story={props.story} />; } 属性展开 function App2() { const props = {firstName: 'Ben', lastName: 'Hector'}; return <Greeting {...props} />; } 函数作为子元素 function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); } 布尔类型、Null 以及 Undefined 将会忽略 8. Render 使用 Render Props 来解决横切关注点 这个组件的问题无法复用;state要复用的话一定要提取出来。 class MouseTracker extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> <h1>移动鼠标!</h1> <p>当前的鼠标位置是 ({this.state.x}, {this.state.y})</p> </div> ); } } 解决方案 Mouse组件提供坐标;Cat组件可以被轻松替换成其他组件。Mouse和Cat都有使用相同的state,是由Mouse传递出来的。Cat在Mouse内被渲染,但是却在Mouse外确定。因此Cat组件很灵活。 这里的render名字只是名字,也可以是children等之类的名称 class Cat extends React.Component { render() { const mouse = this.props.mouse; return ( <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} /> ); } } class Mouse extends React.Component { constructor(props) { super(props); this.handleMouseMove = this.handleMouseMove.bind(this); this.state = { x: 0, y: 0 }; } handleMouseMove(event) { this.setState({ x: event.clientX, y: event.clientY }); } render() { return ( <div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}> {/* Instead of providing a static representation of what <Mouse> renders, use the `render` prop to dynamically determine what to render. */} {this.props.render(this.state)} </div> ); } } class MouseTracker extends React.Component { render() { return ( <div> <h1>移动鼠标!</h1> <Mouse render={mouse => ( <Cat mouse={mouse} /> )}/> </div> ); } } 9. StrictMode 识别不安全的生命周期 关于使用过时字符串 ref API 的警告 关于使用废弃的 findDOMNode 方法的警告 检测意外的副作用 检测过时的 context API
HOOK
不编写 class 的情况下使用 state
解决在组件之间复用状态逻辑很难,Hook 将组件中相互关联的部分拆分成更小的函数
解决难以理解的 class
只能在函数最外层调用 Hook
只能在 React 的函数组件中调用 Hook
Hook 是特殊的函数,让你可以“钩入”React的特性。
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
841. State Hook 在函数组件中调用 useState,参数是设置state的初始值,返回当前 state 以及更新 state 的函数 state可以是多个也可以对象等类型 import React, { useState } from 'react'; function Example() { // 声明一个叫 “count” 的 state 变量。 const [count, setCount] = useState(0); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 2. Effect Hook 在函数组件中调用 useEffect , 组件更新时自动执行。可以通过返回一个函数清除该副作用。 function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); 3. 自定义 Hook 通过useState 和 useEffect 复用逻辑而不是状态 import React, { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; } 4. useContext const locale = useContext(LocaleContext); 不嵌套组件就可以实现context的订阅 const theme = useContext(ThemeContext); const [todos, dispatch] = useReducer(todosReducer);通过 reducer 来管理组件本地的复杂 state。 5. 比较class组件 class组件中 的副作用放在 componentDidMount 或者 componentDidUpdate 中。 useEffect 直接执行副作用,可以在useEffect 里面执行dom 访问。数据获取。可以直接访问 state、props componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } useEffect(() => { document.title = `You clicked ${count} times`; }); 副作用清除。class组件是在componentWillUnmount中。 useEffect 直接返回清除副作用的函数。 class组件在处理订阅时还需要在组件更新时执行清除然后订阅的操作;useEffect每次更新都会自动清除然后添加订阅。 性能优化:useEffect传入第二个参数,只有当参数数组里面的某个state变化才执行副作用。 useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 仅在 count 更改时更新 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。
最后
以上就是负责蓝天最近收集整理的关于react 笔记的全部内容,更多相关react内容请搜索靠谱客的其他文章。
发表评论 取消回复