写在前面:文章适合初级react-hook学习者使用,完整todoList例子及输出
React.memo: 使用场景,父组件中有多个部分组成,其中某些变量的变化并不会直接呈现在子组件中,但子组件中会在set***时同步更新,此部分更新可优化,此时将子组件React.memo处理即可
useCallback: 使用场景,在父组件中传递函数到子组件,如果在父组件中此函数不用useCallback处理,则父组件中无关set更新也会引起子组件的更新,而useCallback之后,只有依赖项set发生变化,子组件才会产生对应的更新
useMemo: 使用场景,在组件中需要对某些属性进行计算,获取计算后的结果,在一些无关的属性set***更新时会 同时刷新 该计算方法,使用useMemo后,该处的值,只有依赖项发生变化才会产生更新,类似于computed
import { Button, message } from 'antd';
import React, { useRef, useState, useCallback, useEffect, useMemo } from 'react'
function TodoList() {
const ipt = useRef(null);
const [todoItems, setTodoItem] = useState([]);
const handleAdd = useCallback(() => {
if (!ipt.current.value) {
message.warning('todo item can not be empty.')
return
}
const isExist = todoItems.find((item) => item.name === ipt.current.value)
if (isExist) {
message.warning('this todo item has exist.')
return
}
const item = {
id: new Date().getTime(),
name: ipt.current.value
};
ipt.current.value = ""
setTodoItem([...todoItems, item]);
}, [todoItems]);
const handleClear = useCallback(() => {
setTodoItem([]);
}, [todoItems]);
const delItem = useCallback((delItem) => {
const items = todoItems.filter((item) => item.id !== delItem.id)
setTodoItem(items);
}, [todoItems])
const edit = useCallback((id, newItem) => {
const items = [];
todoItems.map((item) => {
if (item.id === id) {
items.push({ id: newItem.split('-')[0], name: newItem.split('-')[1] })
} else {
items.push(item)
}
})
setTodoItem(items)
}, [todoItems])
const [text, setText] = useState('')
const handleInput = (e) => {
setText(e.target.value)
const item = {
id: new Date().getTime(),
name: e.target.value
};
// setTodoItem([...todoItems, item]);
}
console.log('render-TodoList')
return (
<div>
<input value={text} onChange={handleInput} />
<input ref={ipt} />
<Button onClick={handleAdd}>add todo </Button>
<Button onClick={handleClear}>clear All</Button>
<div>
<Example />
</div>
{todoItems && todoItems.length > 0 ? todoItems.map((item) => {
return (
<TodoItem data={item} key={item.id} delItem={delItem} edit={edit} />)
}) : ''}
</div>)
}
const Example = React.memo(() => {
const [count, setCount] = useState(1);
const [count1, setCount1] = useState(1);
const [val, setValue] = useState('');
console.log('123-Example');
const getNum = useMemo(() => {
console.log('234-Example-getNum');
return Array.from({ length: count * 10 + count1 * 2 }, (v, i) => i).reduce((a, b) => a + b)
}, [count, count1])
const getNum1 = useCallback(() => {
console.log('234-Example-getNum');
return Array.from({ length: count * 10 + count1 * 2 }, (v, i) => i).reduce((a, b) => a + b)
}, [count, count1])
// useMemo:计算属性,返回的是值 只有依赖项发生修改,useMemo才会更新,可以有多个依赖项
// useCallback: 缓存函数, 在父组件中传递函数到子组件,解决父组件中无关set更新引起的子组件更新,因为每次set更新时
// 非callback函数都会重新生成,由此产生更新触发子组件的更新,而callback函数则为缓存函数,自有依赖项发生
// 变化,子组件才会触发更新动作.
return <div>
<h4>总和:{getNum}</h4>
<div>
<Child getNum={getNum1} />
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount1(count1 + 1)}>+2</button>
<input value={val} onChange={event => setValue(event.target.value)} />
</div>
</div>;
})
const Child = React.memo(({ getNum }) => {
console.log('234-Example-getNum-Child');
return <h4>总和:{getNum()}</h4>
})
// React.memo: 解决在同一个父组件中,屏蔽与子组件无关的set更新而引起的子组件刷新
const TodoItem = React.memo((props) => {
console.log('render-todoItem')
const item = props.data;
const itemRef = useRef('');
const [showInput, setShowInput] = useState(true);
const handleEditable = useCallback(() => {
if (!showInput) {
props.edit(item.id, itemRef.current.value)
}
setShowInput(!showInput)
}, [showInput])
useEffect(() => {
if (!showInput) {
itemRef.current.value = item.id + '-' + item.name
}
}, [showInput])
return (
<div>
{showInput ?
<span>{item.id}-{item.name}</span>
:
<input ref={itemRef} />
}
<Button onClick={handleEditable}>{showInput ? 'edit' : 'save'}</Button>
<Button onClick={() => props.delItem(item)}>delete</Button>
</div>
)
}
)
export default TodoList;