一、开发环境及创建项目
# 一、开发环境及创建项目
Node : 17.3.0
创建项目:npx create-react-app my-app
进入项目:cd my-app
启动项目:npm start
# 二、useState
useState方法:接受一个参数,可以为默认值,也可以为一个函数。
import logo from './logo.svg';
import './App.css';
import React,{ useState } from 'react'
function App() {
const [data,setData] = useState([1,2,3,4,5])
return (
<div className="App">
{
data.map((item,index) => <div key={index}>{item+1}</div>)
}
</div>
);
}
export default App;
# 三、useEffect
通过 useEffect
副作用,mock请求一个接口数据
import React, { useEffect, useState } from 'react'
// 模拟数据接口,3 秒钟返回数据。
const getList = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([6, 7, 8, 9, 10])
}, 3000)
})
}
function App() {
const [data, setData] = useState([1, 2, 3, 4, 5])
useEffect(() => {
(async () => {
const data = await getList()
console.log('data', data)
setData(data)
})()
})
return (
<div className="App">
{
data.map((item, index) => <span key={index}>{item}</span>)
}
</div>
)
}
export default App
函数组件默认进来之后,会执行 useEffect
中的回调函数,但是当 setData
执行之后,App
组件再次刷新,刷新之后会再次执行 useEffect
的回调函数,这便会形成一个可怕的死循环,回调函数会一直被这样执行下去。
所以我们如果传一个空数组 []
,则该副作用只会在组件渲染的时候,执行一次。
# 三、自定义hook
抽离成一个自定义 hook
,方便在多个地方调用,新建 useApi.js
// useApi.js
import React, { useEffect, useState } from 'react'
// 模拟请求
const getList = (query) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('query', query)
resolve([6, 7, 8, 9, 10])
}, 3000)
})
}
// 自定义 hook
const useApi = () => {
const [data, setData] = useState([1, 2, 3, 4, 5])
const [query, setQuery] = useState('')
useEffect(() => {
(async () => {
const data = await getList(query)
// console.log('data', data)
setData(data)
})()
}, [query])
// 将data数据 设置请求参数的方法抛出
return [{ data }, setQuery];
}
export default useApi
// App.js
import './App.css';
import React, { useState, useEffect } from 'react'
import useApi from './useApi';
function App() {
const [{data},setQuery] = useApi()
return (
<div className="App">
{data.map((item, index) => <span key={index}>{item}</span>)}
<input onChange={(e) => setQuery(e.target.value)} type='text' placeholder='...' />
</div>
);
}
export default App;
自定义hook,可以将请求逻辑提取出来公用,Class类组件所不能做到的。
# 四、useMemo
修改 App.jsx
,在内部新增一个子组件,子组件接收父组件传进来的一个对象,作为子组件的 useEffect
的第二个依赖参数。
import './App.css';
import React, { useState, useEffect } from 'react'
function Child({ data }) {
useEffect(() => {
console.log("search condition", data)
}, [data])
return <div>child</div>
}
function App() {
// const [{data},setQuery] = useApi()
const [name, setName] = useState('')
const [phone, setPhone] = useState('')
const [kw, setKw] = useState('')
const data = {
name,
phone
}
return (
<div className="App">
<input onChange={(e) => setName(e.target.value)} type='text' placeholder='name' />
<input onChange={(e) => setPhone(e.target.value)} type='text' placeholder='phone' />
<input onChange={(e) => setKw(e.target.value)} type='text' placeholder='key word' />
<Child data={data}/>
</div>
);
}
export default App;
我们只监听了 name
和 phone
两个参数,但是我们修改关键词输入框,还是会有打印结果。
子组件并没有监听 kw
的变化,但是结果却是子组件也被触发渲染了。原因其实是我们在父组件重新 setKw
之后,data
值和未作修改 kw
前的值已经不一样了。data
的值并没有变化,为什么说它已经不一样了呢?
通过 useMemo
将 data
包装一下,告诉 data
它需要监听的值。
import './App.css';
import React, { useState, useEffect,useMemo } from 'react'
import useApi from './useApi';
function Child({ data }) {
useEffect(() => {
console.log("search condition", data)
}, [data])
return <div>child</div>
}
function App() {
// const [{data},setQuery] = useApi()
const [name, setName] = useState('')
const [phone, setPhone] = useState('')
const [kw, setKw] = useState('')
const data = useMemo(()=>({
name,
phone
}),[name,phone])
return (
<div className="App">
<input onChange={(e) => setName(e.target.value)} type='text' placeholder='name' />
<input onChange={(e) => setPhone(e.target.value)} type='text' placeholder='phone' />
<input onChange={(e) => setKw(e.target.value)} type='text' placeholder='key word' />
<Child data={data}/>
</div>
);
}
export default App;
useMemo
的作用,它相当于把父组件需要传递的参数做了一个标记,无论父组件其他状态更新任何值,都不会影响要传递给子组件的对象。
# 五、useCallback
useCallback
也是和 useMemo
有类似的功能
import React, { useEffect, useState, useCallback } from 'react'
function Child({ callback }) {
useEffect(() => {
callback()
}, [callback])
return <div>子组件</div>
}
function App() {
const [name, setName] = useState('')
const [phone, setPhone] = useState('')
const [kw, setKw] = useState('')
const callback = () => {
console.log('我是callback')
}
return (
<div className="App">
<input onChange={(e) => setName(e.target.value)} type="text" placeholder='请输入姓名' />
<input onChange={(e) => setPhone(e.target.value)} type="text" placeholder='请输入电话' />
<input onChange={(e) => setKw(e.target.value)} type="text" placeholder='请输入关键词' />
<Child callback={callback} />
</div>
)
}
export default App
useCallback 的第二个参数同
useEffect
和useMemo
的第二个参数,它是用于监听你需要监听的变量,如在数组内添加name
、phone
、kw
等参数,当改变其中有个,都会触发子组件副作用的执行。
useMemo
和 useCallback
,都能为「重复渲染」这个问题,提供很好的帮助。