我有一个React表单和正确管理状态的问题。我有一个表单(模态)的时间输入字段。初始值被设置为getInitialState中的状态变量,并从父组件传入。这本身就很好。
当我想通过父组件更新默认的start_time值时,问题就来了。更新本身在父组件中通过setState start_time: new_time进行。然而,在我的表单中,默认的start_time值从未改变,因为它只在getInitialState中定义了一次。
我尝试使用componentWillUpdate强制通过setState start_time: next_props改变状态。start_time,这实际上是工作的,但它给了我Uncaught RangeError:最大调用堆栈大小超过错误。
我的问题是,在这种情况下,更新状态的正确方法是什么?我是不是想错了?
当前代码:
@ModalBody = React.createClass
getInitialState: ->
start_time: @props.start_time.format("HH:mm")
#works but takes long and causes:
#"Uncaught RangeError: Maximum call stack size exceeded"
componentWillUpdate: (next_props, next_state) ->
@setState(start_time: next_props.start_time.format("HH:mm"))
fieldChanged: (fieldName, event) ->
stateUpdate = {}
stateUpdate[fieldName] = event.target.value
@setState(stateUpdate)
render: ->
React.DOM.div
className: "modal-body"
React.DOM.form null,
React.createElement FormLabelInputField,
type: "time"
id: "start_time"
label_name: "Start Time"
value: @state.start_time
onChange: @fieldChanged.bind(null, "start_time")
@FormLabelInputField = React.createClass
render: ->
React.DOM.div
className: "form-group"
React.DOM.label
htmlFor: @props.id
@props.label_name + ": "
React.DOM.input
className: "form-control"
type: @props.type
id: @props.id
value: @props.value
onChange: @props.onChange
componentWillReceiveProps已弃用,因为使用它“经常会导致错误和不一致”。
如果外部发生了变化,请考虑使用key完全重置子组件。
为子组件提供键道具可以确保每当key的值从外部改变时,该组件都会被重新呈现。例如,
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
关于其性能:
虽然这听起来很慢,但性能差异通常是微不足道的。如果组件具有运行在更新上的繁重逻辑,则使用键甚至可以更快,因为该子树的差分被绕过了。
我使用功能组件和useEffect钩子提出了以下解决方案:
它通过使用useEffect钩子监视props中的控制属性来工作
const { useEffect, useState } = React
const Child = (props) => {
const [bgColor, setBgColor] = useState(props.bgColor);
const { children } = props;
useEffect(() => {
setBgColor(props.bgColor);
}, [props.bgColor]);
return (
<div style={{ height: "100px", width: "100px", backgroundColor: bgColor }}>{children}</div>
)
}
const Parent = (props) => {
const [childControllingProp, setChildControllingProp] = useState(props.childControllingProp);
const { title } = props;
const inputRef = React.createRef();
return (
<>
<input ref={inputRef} type="text" onChange={() => setChildControllingProp(inputRef.current.value)}/>
<Child bgColor={childControllingProp}>{title}</Child>
</>
)
}
$(document).ready(() => {
ReactDOM.render(
<Parent title="Title" childControllingProp="blue"/>,
document.querySelector("#root")
);
})
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<div id="root"></div>
钩子的新方法是使用useEffect而不是旧的componentWillReceiveProps:
componentWillReceiveProps(nextProps) {
// You don't have to do this check first, but it can help prevent an unneeded render
if (nextProps.startTime !== this.state.startTime) {
this.setState({ startTime: nextProps.startTime });
}
}
在一个功能性的钩子驱动组件中变成如下:
// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
//
useEffect(() => {
if (props.startTime !== startTime) {
setStartTime(props.startTime);
}
}, [props.startTime]);
我们使用setState设置状态,使用useEffect检查对指定道具的更改,并在道具更改时执行更新状态的操作。