setState 有条件地做出反应
Posted
技术标签:
【中文标题】setState 有条件地做出反应【英文标题】:setState conditionally in react 【发布时间】:2019-03-20 16:41:15 【问题描述】:我知道有些反应,但我陷入了一个奇怪的境地。
我有两个输入和一个按钮, 当两个输入都不为空时,应启用该按钮。 所以,我为每个输入值使用了一个 state 属性,还有一个属性告诉我两个输入是否都有值:
this.state =
title: '',
time :'',
enabled : false
我也为每个输入设置了一个 onChange 来相应地设置 State:
<input type="text" id="time" name="time" onChange=this.onChange.bind(this) value=this.state.time></input>
<input type="text" id="title" name="title" onChange=this.onChange.bind(this) value=this.state.title></input>
而onChange是这样的
onChange(e)
this.setState(
[e.target.id] : e.target.value,
enabled : ( (this.state.title==='' || this.state.time==='' ) ? false : true)
);
问题在于 setState 查看之前的状态,而 enable 总是落后一步,所以如果我在第一个输入 X 并在第二个输入 Y,那么 enabled 仍然是 false。
我设法通过使用 setTimeout 并在其中插入第二行来解决它,但对我来说它看起来不对。
onChange(e)
this.setState(
[e.target.id] : e.target.value,
);
setTimeout(() =>
this.setState(
enabled : ( (this.state.title==='' || this.state.time==='' ) ? false : true)
);
, 0);
有更好的解决方案吗?
【问题讨论】:
【参考方案1】:有更好的解决方案吗?
有 3 种解决方案供您实现,只需选择一种
解决方案 1:使用componentDidUpdate()
class App extends React.Component
constructor(props)
super(props);
this.state =
title: "",
time: "",
enabled: false
;
onChange(e)
this.setState(
[e.target.id]: e.target.value
);
componentDidUpdate(prevProps, prevState)
if (
prevState.time !== this.state.time ||
prevState.title !== this.state.title
)
if (this.state.title && this.state.time)
this.setState( enabled: true );
else
this.setState( enabled: false );
render()
return (
<React.Fragment>
<input
type="text"
id="time"
name="time"
onChange=this.onChange.bind(this)
value=this.state.time
/>
<input
type="text"
id="title"
name="title"
onChange=this.onChange.bind(this)
value=this.state.title
/>
<button disabled=!this.state.enabled>Button</button>
</React.Fragment>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
解决方案 2:使用 setState
回调
class App extends React.Component
constructor(props)
super(props);
this.state =
title: "",
time: "",
enabled: false
;
onChange(e)
this.setState([e.target.id]: e.target.value,
() =>
if (this.state.title && this.state.time)
this.setState( enabled: true );
else
this.setState( enabled: false );
);
render()
return (
<React.Fragment>
<input
type="text"
id="time"
name="time"
onChange=this.onChange.bind(this)
value=this.state.time
/>
<input
type="text"
id="title"
name="title"
onChange=this.onChange.bind(this)
value=this.state.title
/>
<button disabled=!this.state.enabled>Button</button>
</React.Fragment>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
解决方案 3:根据title
和time
计算enabled
class App extends React.Component
constructor(props)
super(props);
this.state =
title: "",
time: ""
;
onChange(e)
this.setState(
[e.target.id]: e.target.value
);
render()
const enabled = this.state.time && this.state.title;
return (
<React.Fragment>
<input
type="text"
id="time"
name="time"
onChange=this.onChange.bind(this)
value=this.state.time
/>
<input
type="text"
id="title"
name="title"
onChange=this.onChange.bind(this)
value=this.state.title
/>
<button disabled=!enabled>Button</button>
</React.Fragment>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
【讨论】:
【参考方案2】:首先,启用状态不需要维护,因为它可以从其他状态值派生出来,可以直接在渲染中完成
onChange(e)
this.setState(
[e.target.id] : e.target.value,
);
render()
const enabled = this.state.title !== "" && this.state.time !== "";
return (
<div>
<input type="text" id="time" name="time" onChange=this.onChange.bind(this) value=this.state.time></input>
<input type="text" id="title" name="title" onChange=this.onChange.bind(this) value=this.state.title></input>
<button disabled=!enabled>Button</button>
</div>
)
【讨论】:
【参考方案3】:@Shubham Khatri 有正确的答案(您实际上并不需要该州的 enabled
属性),但重要的是要了解该州发生了什么以及为什么您使用 setTimeout
的 hack
有效(在我看来很幸运)。
来自official docs
setState() 并不总是立即更新组件。有可能 批处理或推迟更新。这使得阅读 this.state 就在调用 setState() 之后,这是一个潜在的陷阱。相反,使用 componentDidUpdate 或 setState 回调(setState(updater, 回调)),其中任何一个都保证在更新后触发 已申请。
另外,state updates may be asynchronous:
React 可以将多个 setState() 调用批处理到单个更新中 性能。
因为 this.props 和 this.state 可能是异步更新的,所以你 不应依赖它们的值来计算下一个状态。
我希望这可以澄清您的问题。
【讨论】:
【参考方案4】:如果您想在当前更改发生后设置状态,您可以将第二个参数(回调)传递给 setState,这将在已设置状态并再次设置您的状态后发生。它会导致额外的重新渲染,但似乎这就是你要问的。这只是为了让您知道在当前状态更改发生后如何做某事...
但是,我会同意 Shubham Khatri 的回答。那会更合乎逻辑。
【讨论】:
以上是关于setState 有条件地做出反应的主要内容,如果未能解决你的问题,请参考以下文章
对 useEffect 挂钩内的多个 setState() 调用做出反应批量更新
如何切换 css 类以与 setState Hook 做出反应