可以说我有一个父组件和一个子组件。父组件由几个子组件组成。父组件保存并管理一个非常复杂且深层的数据对象。每个子组件都提供用于管理各种子对象和主数据对象属性的UI。每当子组件更改数据对象层次结构中的属性值时,该更改就需要冒泡到主数据对象。
这是我可以通过传入回调对象在子组件类中实现的方法...
<div>
<button onClick={e => this.setState({propA: e.target.value}, () => props.onChangePropA(this.state.propA)}>Prop A</button>
<button onClick={e => this.setState({propB: e.target.value}, () => props.onChangePropB(this.state.propB)}>Prop B</button>
</div>
与我认为我需要使用钩子进行比较。我看到的主要问题是状态更改完成后没有回调选项。因此,我必须在useEffect中对其进行检测,并找出刚刚更改的属性...
let prevPropA = props.propA;
let prevPropB = props.propB;
const [propA, setPropA] = useState(props.propA);
const [propB, setPropB] = useState(props.propB);
useEffect(() => {
if (prevPropA != propA) props.onChangePropA(propA);
if (prevPropB != propB) props.onChangePropB(propB);
});
<div>
<button onClick={e => {prevPropA = propA; setPropA(e.target.value)}}>Prop A</button>
<button onClick={e => {prevPropB = propB; setPropB(e.target.value)}}>Prop B</button>
</div>
我看到这种方法变得非常麻烦和凌乱。是否有更健壮/正确的方法来完成此任务?
谢谢
================================================ =============
以下是根据Shubham的答案更新的示例代码, 瑞安的反馈。 Shubham按要求回答了问题,但 瑞安建议我举一个更彻底的例子来确保 我为正确的答案提供正确的信息。 这是更贴近我的现实世界的示例代码 情况...虽然仍然是一个简化的示例。 父组件管理来自用户的评论。想像 他们可以创建新评论并选择日期或日期范围。 他们还可以更新现有评论。我把日期 和日期范围选择器。 因此,父注释管理器组件需要具备此功能 创建/加载评论并将相关日期向下传递到 日期选择器组件。然后,用户可以更改日期 这些值需要传播回父注释 管理员,以后再发送到服务器并保存。 如此看来,属性值(日期等)是双向流动的 可以随时从任一端进行更改。 注意:此新示例使用类似于以下方法的更新 Shubham根据我最初的问题提出了建议。
================================================ =============
const DateTimeRangeSelector = (props) =>
{
const [contextDateStart, setContextDateStart] = useState(props.contextDateStart);
const [contextDateEnd, setContextDateEnd] = useState(props.contextDateEnd);
const [contextDateOnly, setContextDateOnly] = useState(props.contextDateOnly);
const [contextDateHasRange, setContextDateHasRange] = useState(props.contextDateHasRange);
useEffect(() => { setContextDateStart(props.contextDateStart); }, [ props.contextDateStart ]);
useEffect(() => { if (contextDateStart !== undefined) props.onChangeContextDateStart(contextDateStart); }, [ contextDateStart ]);
useEffect(() => { setContextDateEnd(props.contextDateEnd); }, [ props.contextDateEnd ]);
useEffect(() => { if (contextDateEnd !== undefined) props.onChangeContextDateEnd(contextDateEnd); }, [ contextDateEnd ]);
useEffect(() => { setContextDateOnly(props.contextDateOnly); }, [ props.contextDateOnly ]);
useEffect(() => { if (contextDateOnly !== undefined) props.onChangeContextDateOnly(contextDateOnly); }, [ contextDateOnly ]);
useEffect(() => { setContextDateHasRange(props.contextDateHasRange); }, [ props.contextDateHasRange ]);
useEffect(() => { if (contextDateHasRange !== undefined) props.onChangeContextDateHasRange(contextDateHasRange); }, [ contextDateHasRange ]);
return <div>
<ToggleButtonGroup
exclusive={false}
value={(contextDateHasRange === true) ? ['range'] : []}
selected={true}
onChange={(event, value) => setContextDateHasRange(value.some(item => item === 'range'))}
>
<ToggleButton value='range' title='Specify a date range' >
<FontAwesomeIcon icon='arrows-alt-h' size='lg' />
</ToggleButton>
</ToggleButtonGroup>
{
(contextDateHasRange === true)
?
<DateTimeRangePicker
range={[contextDateStart, contextDateEnd]}
onChangeRange={val => { setContextDateStart(val[0]); setContextDateEnd(val[1]); }}
onChangeShowTime={ val => setContextDateOnly(! val) }
/>
:
<DateTimePicker
selectedDate={contextDateStart}
onChange={val => setContextDateStart(val)}
showTime={! contextDateOnly}
/>
}
</div>
}
const CommentEntry = (props) =>
{
const [activeComment, setActiveComment] = useState(null);
const createComment = () =>
{
return {uid: uuidv4(), content: '', contextDateHasRange: false, contextDateOnly: false, contextDateStart: null, contextDateEnd: null};
}
const editComment = () =>
{
return loadCommentFromSomewhere();
}
const newComment = () =>
{
setActiveComment(createComment());
}
const clearComment = () =>
{
setActiveComment(null);
}
return (
<div>
<Button onClick={() => newComment()} variant="contained">
New Comment
</Button>
<Button onClick={() => editComment()} variant="contained">
Edit Comment
</Button>
{
activeComment !== null &&
<div>
<TextField
value={(activeComment) ? activeComment.content: ''}
label="Enter comment..."
onChange={(event) => { setActiveComment({...activeComment, content: event.currentTarget.value, }) }}
/>
<DateTimeRangeSelector
onChange={(val) => setActiveComment(val)}
contextDateStart={activeComment.contextDateStart}
onChangeContextDateStart={val => activeComment.contextDateStart = val}
contextDateEnd={activeComment.contextDateEnd}
onChangeContextDateEnd={val => activeComment.contextDateEnd = val}
contextDateOnly={activeComment.contextDateOnly}
onChangeContextDateOnly={val => activeComment.contextDateOnly = val}
contextDateHasRange={activeComment.contextDateHasRange}
onChangeContextDateHasRange={val => activeComment.contextDateHasRange = val}
/>
<Button onClick={() => clearComment()} variant="contained">
Cancel
</Button>
<Button color='primary' onClick={() => httpPostJson('my-url', activeComment, () => console.log('saved'))} variant="contained" >
<SaveIcon/> Save
</Button>
</div>
}
</div>
);
}
答案 0 :(得分:1)
useEffect
使用第二个参数,该参数指示何时执行效果。您可以将状态值传递给它,以便它在状态更新时执行。另外,您的代码中可以有多个useEffect
钩子
const [propA, setPropA] = useState(props.propA);
const [propB, setPropB] = useState(props.propB);
useEffect(() => {
props.onChangePropA(propA);
}, [propA]);
useEffect(() => {
props.onChangePropB(propB);
}, [propB]);
<div>
<button onClick={e => {setPropA(e.target.value)}}>Prop A</button>
<button onClick={e => {setPropB(e.target.value)}}>Prop B</button>
</div>