Javascript 使用useCallback并使用前一个状态作为参数设置新对象状态

Javascript 使用useCallback并使用前一个状态作为参数设置新对象状态,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,考虑这个基本表单字段组件,它带有一个自定义表单挂钩来处理输入更改: import React, { useState, useCallback } from 'react'; const useFormInputs = (initialState = {})=> { const [values, setValues] = useState(initialState); const handleChange = useCallback(({ target: { name,

考虑这个基本表单字段组件,它带有一个自定义表单挂钩来处理输入更改:

import React, { useState, useCallback } from 'react';

const useFormInputs = (initialState = {})=> {
    const [values, setValues] = useState(initialState);
    const handleChange = useCallback(({ target: { name, value } }) => {
        setValues(prev => ({ ...prev, [name]: value }));
    }, []);
    const resetFields = useCallback(() =>
        setValues(initialState), [initialState]);
    return [values, handleChange, resetFields];
};

const formFields = [
    { name: 'text', placeholder: 'Enter text...', type: 'text', text: 'Text' },
    { name: 'amount', placeholder: 'Enter Amount...', type: 'number',
        text: 'Amount (negative - expense, positive - income)' }
];

export const AddTransaction = () => {
    const [values, handleChange, resetFields] = useFormInputs({
        text: '', amount: ''
    });
    return <>
        <h3>Add new transaction</h3>
        <form>
            {formFields.map(({ text, name, ...attributes }) => {
                const inputProps = { ...attributes, name };
                return <div key={name} className="form-control">
                    <label htmlFor={name}>{text}</label>
                    <input {...inputProps} value={values[name]}
                        onChange={handleChange} />
                </div>;
            })}
            <button className="btn">Add transaction</button>
        </form>
        <button className="btn" onClick={resetFields}>Reset fields</button>
    </>;
};
import React,{useState,useCallback}来自“React”;
const useFormInputs=(initialState={})=>{
const[values,setValues]=useState(initialState);
const handleChange=useCallback({target:{name,value}}})=>{
setValues(prev=>({…prev,[名称]:value}));
}, []);
const resetFields=useCallback(()=>
设置值(初始状态),[initialState];
返回[值、handleChange、resetFields];
};
const formFields=[
{name:'text',占位符:'Enter text…',键入:'text',text:'text'},
{名称:'金额',占位符:'输入金额…',键入:'数字',
文本:'金额(负-费用,正-收入)}
];
export const AddTransaction=()=>{
常量[值、句柄更改、重置字段]=使用输入({
文本:“”,金额:“”
});
返回
添加新事务
{formFields.map({text,name,…attributes})=>{
constInputProps={…属性,名称};
返回
{text}
;
})}
添加事务
重置字段
;
};
  • 对于我来说,使用useCallback在自定义钩子中缓存函数真的有什么原因/优势吗?我读了文档,但我就是不明白useCallback这个用法背后的意思。它是如何在渲染之间记忆函数的?ti到底是如何工作的,我应该使用它吗

  • 在同一个自定义钩子中,您可以看到新值状态正在更新,方法是扩展以前的状态并创建一个新对象,如下所示:
    setValues(prev=>({…prev,[名称]:value}))
    如果我这样做会有什么不同吗<代码>设置值({…prev[name]:value})
    就我所知,看起来没有什么区别吧?我只是直接进入州政府。。我错了吗

  • 你的第一个问题:

    在您的情况下,这并不重要,因为所有内容都在同一个组件中呈现。如果您有一个事件处理程序列表,那么useCallback可以为您保存一些渲染

    在下面的示例中,前两个项目使用onClick进行渲染,每次应用程序重新渲染时都会重新创建onClick。这不仅会导致项目重新呈现,还会导致虚拟DOM比较失败,并且会在DOM中重新创建ITM(昂贵的操作)

    最后两个项目获得一个onClick,该onClick是在应用程序装载时创建的,而不是在应用程序重新渲染时重新创建的,因此它们永远不会重新渲染

    const{useState,useCallback,useRef,memo}=React;
    const Item=memo(函数项({onClick,id}){
    呈现常量=useRef(0);
    rendered.current++;
    返回(
    {id}:呈现{rendered.current}次
    );
    });
    常量应用=()=>{
    const[message,setMessage]=useState(“”);
    const onClick=(e)=>
    设置消息(
    '上次单击'+e.target.getAttribute(''u id'))
    );
    const memOnClick=useCallback(onClick,[]);
    返回(
    {message}
    {[1,2].map((id)=>(
    ))}
    {[1,2].map((id)=>(
    ))}
    );
    };
    ReactDOM.render(,document.getElementById('root'))
    
    
    
    至于第二个问题,是的,我的意思是
    设置值({…值,[名称]:值})
    。假设我没有使用useCallback来包装它,这与在useCallback中进行
    setValues(prev=>({…prev[name]:value}))
    有什么区别吗?@kevkev如果你想在useCallback中包装
    setValues({…values[name]:value})
    ,那么你就不需要创建一个依赖项
    values
    ,忽略此依赖项将创建一个过时的闭包,如上面的示例所示(反例)。如果不将其包装在useCallback或任何其他备忘录挂钩中,则没有区别,但这样会不必要地重新呈现子组件。它不会出现在您的示例中,因为您没有子组件。