我对反应组件中的“一次性操作”有疑问。想象一下,例如我想将一些元素滚动到某个位置,或者重置内部反应状态。
到目前为止,我一直在使用布尔标志(例如doAction: true
)和更新操作(例如setDoActionBackToFalse
)的组合来做到这一点,但这似乎太复杂了。有没有人对此有任何好的解决方案?
注意:该动作实际上可以在组件的生命周期内多次发生,但每次必须专门触发并且只发生一次(不会在每次重新渲染时都发生)。例如。滚动到滚动窗格中的每个新添加的项目。
我创造了一个小小提琴,使问题更加明显: https://jsfiddle.net/martinkadlec/et74rkLk/1/ 这使用布尔标志方法。
答案 0 :(得分:0)
将您的动作处理程序包含在更高级别的函数中,该函数仅调用它们一次。 Lodash有once
。 Ramda有it too。
滚动场景的更新....滚动是必须由DOM API启动的副作用。你可以编写一个包含其中任何组件的HOC -
function OnFocusExtender(Wrapped){
return class ExtendedFocus{
focus = _.once(elem => elem && elem.focus && elem.focus());
render(){
return <Wrapped ref={this.focus} {...this.props} />;
}
}
}
然后你可以在你的代码中使用它,如 -
render(){
let FocusedComponent = FocusExtender(YourComponent);
return <FocusedComponent a={"blah"} b={blah} />
}
更新了通用的副作用方法:
HOC:
function WelcomingParty(...party)=>(Wrapped)=>{
return class ExtendWelcome{
// Every host in the welcoming party greets
// the guest :)
welcome = (ref) => party.forEach(host => host(ref));
render(){
return <Wrapped ref={this.welcome} {...this.props} />;
}
}
}
用法:
let hostFn = (fn)=>(ref)=> ref && (typeof ref[fn] == "function") && ref[fn](),
hosts = ["focus", "scrollIntoView"].map(hostFn);
render(){
let Component = WelcomingParty(...hosts)(YourComponent);
return <Component a={"blah"} b={blah} />
}
答案 1 :(得分:0)
我认为你可以使用componentDidMount生命周期钩子。在挂载组件后立即调用此挂钩,并且可以在其中访问DOM。
您也可以拨打“一次性行动”。在组件constructor
中,但在安装组件之前和初始渲染之前调用它,这样您就无法在那里访问DOM。
所以你可以在constructor
中初始化组件状态(根据React docs:构造函数是初始化状态的正确位置)但是你不能滚动一些constructor
中某个位置的元素,因为您无法访问其中的组件DOM元素。
总结:状态初始化应该在constructor
进行,同时进行一次动作&#39;操纵DOM应该在componentDidMount
。
答案 2 :(得分:0)
自从我问这个问题已经有一段时间了,从那时起,我发现只要“一次性操作”实际上并没有重新渲染组件,而是只是修改了一些浏览器状态(例如焦点,滚动位置,等),人们通常倾向于通过使用类方法并使用引用从父组件中调用它来解决此问题。
为了说明焦点示例:
class Input extends React.Component {
inputElRef = React.createRef();
focus = () => this.inputElRef.current.focus();
render() {
return (
<input ref={this.inputElRef} />
);
}
}
class Parent extends React.Component {
inputRef = React.createRef();
render() {
return (
<div>
<button onClick={() => this.inputRef.current.focus()}>Focus input</button>
<Input ref={this.inputRef} />
</div>
);
}
}