React
拓展函数式组件的特性
Hooks
- Hooks解决的问题
- 高阶组件为了复用而导致的代码层级复杂
- 生命周期复杂
- 重构成本高
useState
记忆函数(记住组件的状态)
代码示例:
// src/test/Test04.jsx
import React, {useState} from 'react';
export default function Test04() {
const [state, setState] = useState('') // 两者名字可自定义,''为初始值
return (
<div>
<button onClick={() => {
setState('HQSY')
}}>Click
</button>
{state}
</div>
)
}
// src/test/Test04.jsx
import React, {useState} from 'react';
export default function Test04() {
const [text, setText] = useState('')
const [list, setList] = useState(['a', 'b', 'c'])
const handleChange = (evt) => {
setText(evt.target.value)
}
const handleAdd = () => {
setList([...list, text])
setText('')
}
const handleDel = (index) => {
let newList = [...list]
newList.splice(index, 1)
setList(newList)
}
return (
<div>
<input onChange={handleChange} value={text}/>
<button onClick={handleAdd}>add</button>
<ul>
{
list.map((item, index) =>
<li key={item}>{item}
<button onClick={() => {
handleDel(index)
}}>del
</button>
</li>
)
}
</ul>
{!list.length && <div>暂无数据</div>}
</div>
)
}
useEffect
代码示例:
// src/test/Test04.jsx
// public/test.json
import React, {useState, useEffect} from 'react';
import axios from "axios";
export default function Test04() {
const [list, setList] = useState([])
useEffect(() => { // 没有在数组中声明依赖则仅执行一次
axios.get(`/test.json`).then(res => {
console.log(res.data.films)
setList(res.data.films)
})
}, [])
return (
<div>
</div>
)
}
// src/test/Test04.jsx
import React, {useState, useEffect} from 'react';
export default function Test04() {
const [name, setName] = useState('')
useEffect(() => {
setName(name.substring(0, 1).toUpperCase() + name.substring(1))
}, [name])
return (
<div>
{name}
<button onClick={() => {
setName('foo')
}}>Click
</button>
</div>
)
}
// src/test/Test04.jsx
import React, {useState, useEffect} from 'react';
function Child() {
useEffect(() => { // 创建
window.onresize = () => {
console.log('onresize')
}
let timer = setInterval(() => {
console.log('HQSY')
}, 1000)
return () => { // 销毁(有依赖:更新、销毁时执行,无依赖:销毁时只执行一次)
console.log('销毁')
window.onresize = null
clearInterval(timer)
}
}, [])
return <div>Child</div>
}
export default function Test04() {
const [a, setA] = useState(true)
return (
<div>
<button onClick={() => {
setA(false)
}}>del
</button>
{a && <Child/>}
</div>
)
}
useCallback
代码示例:
// src/test/Test04.jsx
// useCallback如果不配置略过的数据,那么将会一直使用缓存时的数据(小规模改变不会引发大规模刷新)
import React, {useState, useCallback} from 'react';
export default function Test04() {
const [text, setText] = useState('')
const [list, setList] = useState(['a', 'b', 'c'])
const handleChange = useCallback((evt) => {
setText(evt.target.value)
}, [])
const handleAdd = useCallback(() => {
setList([...list, text])
setText('')
}, [list, text])
const handleDel = useCallback((index) => {
let newList = [...list]
newList.splice(index, 1)
setList(newList)
}, [list])
return (
<div>
<input onChange={handleChange} value={text}/>
<button onClick={handleAdd}>add</button>
<ul>
{
list.map((item, index) =>
<li key={item}>{item}
<button onClick={() => {
handleDel(index)
}}>del
</button>
</li>
)
}
</ul>
{!list.length && <div>暂无数据</div>}
</div>
)
}
useMemo
代码示例:
// src/test/Test04.jsx
// public/test.json
/*
1. 区别
· useCallback不会执行第一个参数的函数,而是将它返回,而useMemo会执行第一个参数的函数并将执行结果返回
2. 应用
· useCallback生成记忆后的事件函数并传递给子组件使用,而useMemo更适合经过函数计算得到一个确定的值
3. 理解
· useCallback记录的是数据本身,而useMemo记录的是函数计算过后返回的结果
*/
import React, {useState, useMemo, useEffect} from 'react';
import axios from "axios";
export default function Test04() {
const [myText, setMyText] = useState('')
const [cinemaList, setCinemaList] = useState([])
useEffect(() => {
axios.get(`/test.json`).then(res => {
console.log(res.data.films)
setCinemaList(res.data.films)
}).catch(err => console.log(err))
}, [])
const getCinemaList = useMemo(() => {
return (
cinemaList.filter(item => item.name.includes(myText) ||
item.synopsis.includes(myText))
)
}, [cinemaList, myText])
return (
<div>
<input value={myText} onChange={evt => {
setMyText(evt.target.value)
}}/>
{
getCinemaList.map(item =>
<dl key={item.filmId}>
<dt>{item.name}</dt>
<dd>{item.synopsis}</dd>
</dl>
)
}
</div>
)
}
useRef
代码示例:
// src/test/Test04.jsx
/*
1. 绑在DOM上拿到的就是原生DOM节点
2. 绑在组件上拿到的就是组件对象
3. useRef也起到了记忆函数(闭包)的作用
*/
import React, {useRef} from 'react';
export default function Test04() {
const Ref = useRef()
return (
<div>
<input type={'text'} ref={Ref}/>
<button onClick={() => console.log(Ref.current.value)}>Click</button>
</div>
)
}
useContext
代码示例:
// src/test/Test04.jsx
// public/test.json
import React, {useContext, useEffect, useState} from 'react';
import axios from "axios";
import '../css/css02.css'
const GlobalContext = React.createContext()
function FilmItem(props) {
let {name, poster, grade, synopsis} = props
const value = useContext(GlobalContext) // 返回生产者提供的公共服务
return (
<div className={'filmItem'} onClick={() => {
value.changeText(synopsis)
}}>
<img src={poster} alt={name}/>
<h4>{name}</h4>
<div>评分:{grade}</div>
</div>
)
}
function FilmDetail() {
const value = useContext(GlobalContext)
return (
<div className={'FilmDetail'}>
{
value.text
}
</div>
)
}
export default function Test04() {
const [list, setList] = useState([])
const [text, setText] = useState('')
useEffect(() => {
axios.get(`/test.json`).then((res) => {
console.log(res.data.films)
setList(res.data.films)
}).catch((err) => {
console.log(err)
})
}, [])
return (
<GlobalContext.Provider value={{
text: text,
changeText: (value) => {
setText(value)
}
}}>
<div>
{
list.map((item) =>
<FilmItem key={item.filmId} {...item}/>
)
}
<FilmDetail/>
</div>
</GlobalContext.Provider>
)
}
useReducer
代码示例:
// src/test/Test04.jsx
import React, {useReducer} from "react";
// 处理函数
const reducer = (prevState, action) => { // 分别对应老的状态与type
let newState = {...prevState} // 不要直接修改老的状态
switch (action.type) {
case 'HQSY-minus':
newState.count--
return newState
case 'HQSY-add':
newState.count++
return newState
default:
return newState
}
}
// 外部对象
const intialState = {
count: 0
}
export default function Test04() {
const [state, dispath] = useReducer(reducer, intialState)
return (
<div>
<button onClick={() => {
dispath({ // 会顺便调用reducer函数
type: 'HQSY-minus'
})
}}>-
</button>
{state.count}
<button onClick={() => {
dispath({
type: 'HQSY-add'
})
}}>+
</button>
</div>
)
}
// src/test/Test04.jsx
/*
1. 与useContext配合使用
2. useReducer只能在函数中使用,且最好是根组件函数中(单一数据源),如果分别在子组件中,那么将返回不同的数据(互不干扰)
3. useReducer不支持异步,而Redux支持异步
*/
import React, {useContext, useReducer} from "react";
const reducer = (prevState, action) => {
let newState = {...prevState}
switch (action.type) {
case 'a':
newState.a = action.value
return newState
case 'b':
newState.b = action.value
return newState
default:
return prevState
}
}
const intialState = {
a: 'a',
b: 'b'
}
function Child01() {
const {dispath} = useContext(GlobalContext)
return (
<div style={{background: 'gray'}}>
<button onClick={() => {
dispath({
type: 'a',
value: 'c'
})
}}>改变a
</button>
<button onClick={() => {
dispath({
type: 'b',
value: 'd'
})
}}>改变b
</button>
</div>
)
}
function Child02() {
const {state} = useContext(GlobalContext)
return (
<div style={{background: 'red'}}>
{state.a}
</div>
)
}
function Child03() {
const {state} = useContext(GlobalContext)
return (
<div style={{background: 'yellow'}}>
{state.b}
</div>
)
}
const GlobalContext = React.createContext()
export default function Test04() {
const [state, dispath] = useReducer(reducer, intialState)
return (
<GlobalContext.Provider value={
{
state, // 对象简写
dispath
}
}>
<div>
<Child01/>
<Child02/>
<Child03/>
</div>
</GlobalContext.Provider>
)
}
自定义Hooks
代码示例:
// src/test/Test04.jsx
// public/test.json
/*
1. 作用
· 共享逻辑(大函数包含小函数)
2. 规范
· 必须以use开头
*/
import React, {useState, useMemo, useEffect} from 'react';
import axios from "axios";
function useCinemaList() {
const [cinemaList, setCinemaList] = useState([])
useEffect(() => {
axios.get(`/test.json`).then(res => {
console.log(res.data.films)
setCinemaList(res.data.films)
}).catch(err => console.log(err))
}, [])
return {
cinemaList
}
}
function useFilter(cinemaList, myText) {
const getCinemaList = useMemo(() => {
return (
cinemaList.filter(item => item.name.includes(myText) ||
item.synopsis.includes(myText))
)
}, [cinemaList, myText])
return {
getCinemaList
}
}
export default function Test04() {
const [myText, setMyText] = useState('')
const {cinemaList} = useCinemaList()
const {getCinemaList} = useFilter(cinemaList, myText)
return (
<div>
<input value={myText} onChange={evt => {
setMyText(evt.target.value)
}}/>
{
getCinemaList.map(item =>
<dl key={item.filmId}>
<dt>{item.name}</dt>
<dd>{item.synopsis}</dd>
</dl>
)
}
</div>
)
}