React.js中useState钩子setState用法疑惑求助:const声明状态可更新、属性丢失及扩展运算符使用问题
咱们一个个来拆解你的疑惑,帮你理清useState的正确用法:
1. 为什么const声明的dateForm能通过setDateForm更新?
你这里的误区是把变量的引用和状态值搞混啦。const [dateForm, setDateForm] = useState(initialDateFormState);里的dateForm是个const变量,意味着你不能直接给它赋值(比如dateForm = {}这种操作肯定报错),但setDateForm并不是直接修改这个变量——它是告诉React:“我要更新状态啦”。
当你调用setDateForm时,React会重新渲染组件,在新的渲染周期里,dateForm会被赋值为你传入的新状态对象。原来的dateForm变量本身并没有被修改,只是组件重新执行时,它拿到了新的状态值而已,完全符合const的规则。
2. 为什么time属性会消失?
因为你调用setDateForm({ date: e.target.value })的时候,直接传入了一个只包含date属性的新对象,没有把原来dateForm里的time属性保留下来。React的useState更新是替换式的,不是合并式的(和类组件的this.setState不一样)。
要保留原来的属性,你需要用扩展运算符把旧状态的所有属性都展开,再覆盖需要更新的部分:
setDateForm({ ...dateForm, date: e.target.value });
这样新对象就会包含原来的time属性,同时更新date的值。
3. 为什么setDateform(date: e.target.value)会报错?
首先这个写法本身就不对——你想传入一个对象,但少了大括号,相当于直接传了e.target.value这个字符串,而不是包含date属性的对象。这会导致dateForm从原来的对象变成字符串,而你的组件里可能用dateForm.date来绑定受控输入,当dateForm变成字符串后,dateForm.date就变成undefined,受控输入就失去了它的控制值,所以浏览器会提示“正在尝试将受控组件转换为非受控组件”。
正确的写法要么是传完整的对象(带扩展运算符保留其他属性),要么如果是依赖旧状态计算新状态,可以用函数式更新:
setDateForm(prevState => ({ ...prevState, date: e.target.value }));
内容的提问来源于stack exchange,提问作者anlehoang

