React

拓展函数式组件的特性

Hooks

  1. 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>
    )
}