Javascript 反应:如何在功能组件中创建状态相关功能?
在我的应用程序中,我有如下组件:Javascript 反应:如何在功能组件中创建状态相关功能?,javascript,reactjs,functional-programming,react-functional-component,Javascript,Reactjs,Functional Programming,React Functional Component,在我的应用程序中,我有如下组件: const MyComponent = props => { const { attrOneDefault, attrTwoDefault, formControl } = props; const [inputValue, setInputValue] = useState({ attr_one: attrOneDefault, attr_two: attrTwoDefault });
const MyComponent = props => {
const { attrOneDefault, attrTwoDefault, formControl } = props;
const [inputValue, setInputValue] = useState({
attr_one: attrOneDefault,
attr_two: attrTwoDefault
});
const getValue = ( attr ) => {
return inputValue[attr];
}
const setValue = ( attr, val ) => {
if( attr === 'attr_one' ) {
if( val === 'bar' && getValue(attr) !== 'foo' ) {
val = 'foo bar';
}
}
setInputValue( {...inputValue, [attr]: val} );
}
useEffect( () => {
if( formControl ) {
Object.keys(inputValue).forEach( attribute => {
formControl.subscribeToValueCollecting( attribute, () => {
return getValue(attribute);
});
formControl.subscribeToValueChange( attribute, ( value ) => {
setValue( attribute, value );
return true;
});
});
}
return () => {
if( formControl ) {
Object.keys(inputValue).forEach( attribute => formControl.unsubscribe(attribute) );
}
}
}, []);
return (
<div class="form-field">
<input
type="text"
value={getValue('attr_one')}
onChange={ e => setValue('attr_one', e.target.value)}
/>
<input
type="checkbox"
checked={getValue('attr_two')}
onChange={ e => setValue('attr_two', !!e.target.checked)}
/>
</div>
);
}
2带有useEffect的函数setValue和getValue在首次渲染时不可用:
let getValue, setValue;
useEffect( () => {
getValue = ( attr ) => {
return inputValue[attr];
}
setValue = ( attr, val ) => {
if( attr === 'attr_one' ) {
if( val === 'bar' && getValue(attr) !== 'foo' ) {
val = 'foo bar';
}
}
setInputValue( {...inputValue, [attr]: val} );
}
}, [inputValue]);
试试这个:
const getValue = ( attr ) => {
return inputValue[attr];
}
const getValueRef = useRef(getValue)
const setValue = ( attr, val ) => {
setInputValue( inputValue =>{
if( attr === 'attr_one' ) {
if( val === 'bar' && inputValue[attr] !== 'foo' ) {
val = 'foo bar';
}
}
return {...inputValue, [attr]: val} );
}
}
useEffect(()=>{
getValueRef.current=getValue
})
useEffect( () => {
const getCurrentValue = (attr)=>getValueRef.current(attr)
if( formControl ) {
Object.keys(inputValue).forEach( attribute => {
formControl.subscribeToValueCollecting( attribute, () => {
return getCurrentValue(attribute);
});
formControl.subscribeToValueChange( attribute, ( value ) => {
setValue( attribute, value );
return true;
});
});
}
return () => {
if( formControl ) {
Object.keys(inputValue).forEach( attribute => formControl.unsubscribe(attribute) );
}
}
}, []);
编写代码将逻辑提取到单独的代码单元中。由于您的状态更改部分依赖于前一个状态,因此您应该调用而不是useState,以使实现更容易,并且状态更改是原子的:
const useAccessors = initialState => {
const [state, dispatch] = useReducer((oldState, [attr, val]) => {
if (attr === 'attr_one') {
if (val === 'bar' && getValue(attr) !== 'foo') {
val = 'foo bar';
}
}
// this is important! your reference must be preserved (ew)
oldState[attr] = val;
return oldState;
}, initialState);
const getValue = useCallback(
attr => state[attr],
[state]
);
const setValue = useCallback(
(attr, val) => {
dispatch([attr, val]);
},
[dispatch]
);
return { getValue, setValue, state };
};
我通常不建议修改state对象,但在这种情况下,有必要这样做,否则由于state引用不断变化,useffect将非常低效。如果有人知道如何在不违反规则的情况下解决此问题,请随时在下面发表评论
现在,useEffect从第二个参数中省略了依赖项。虽然有时有这样做的有效用例,但它通常只会导致您当前遇到的问题
让我们将useEffect也移动到自定义挂钩中并修复它:
const useFormControl = (formControl, { getValue, setValue, state }) => {
useEffect(() => {
if (formControl) {
const keys = Object.keys(state);
keys.forEach(attribute => {
formControl.subscribeToValueCollecting(attribute, () => {
return getValue(attribute));
});
formControl.subscribeToValueChange(attribute, value => {
setValue(attribute, value);
return true;
});
});
return () => {
keys.forEach(attribute => {
formControl.unsubscribe(attribute);
});
};
}
}, [formControl, getValue, setValue, state]);
};
由于getValue和setValue是记忆的,并且state是一个常量可变引用,所以唯一实际更改的依赖项是formControl,这很好
综上所述,我们得到:
const MyComponent = props =>
const { attrOneDefault, attrTwoDefault, formControl } = props;
const { getValue, setValue, state } = useAccessors({
attr_one: attrOneDefault,
attr_two: attrTwoDefault
});
useFormControl(formControl, { getValue, setValue, state });
return (
<div class="form-field">
<input
type="text"
value={getValue('attr_one')}
onChange={e => setValue('attr_one', e.target.value)}
/>
<input
type="checkbox"
checked={getValue('attr_two')}
onChange={e => setValue('attr_two', e.target.checked)}
/>
</div>
);
};
为什么你的设定值里面有看似随机的逻辑?这只是个例子——这个函数有一些逻辑。我想让代码更简短,更容易理解。你的useEffect就是捕获你的inputValue,这就是为什么它总是一样的。尝试将inputValue传递到use effect数组[]中。使用formControl的引用回调。订阅。。。listeners@AlexShul我不得不问,您是否实现了SubscribeToValueCollection和subscribeToValueChange方法?如果不是,它们的真正名称是什么,它们来自哪个图书馆?这可能会帮助我避免为了获得合理的性能而改变对象。这个钩子帮助验证表单和提交数据。它从父组件传递。钩子代码:欢迎对性能改进提出任何建议或承诺。
const MyComponent = props =>
const { attrOneDefault, attrTwoDefault, formControl } = props;
const { getValue, setValue, state } = useAccessors({
attr_one: attrOneDefault,
attr_two: attrTwoDefault
});
useFormControl(formControl, { getValue, setValue, state });
return (
<div class="form-field">
<input
type="text"
value={getValue('attr_one')}
onChange={e => setValue('attr_one', e.target.value)}
/>
<input
type="checkbox"
checked={getValue('attr_two')}
onChange={e => setValue('attr_two', e.target.checked)}
/>
</div>
);
};