親コンポーネントと子コンポーネントがあるとしましょう。親コンポーネントは、いくつかの子コンポーネントで構成されています。親コンポーネントは、非常に複雑で深いデータオブジェクトを保持および管理します。各子コンポーネントは、メインデータオブジェクトのさまざまな子オブジェクトとプロパティを管理するための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の回答とRyanのフィードバックに基づいて更新されたサンプルコードです。Shubhamは質問に答えましたが、Ryanは、正しい答えに対して正しい情報を提供できるように、より徹底的な例を示すことを提案しています。これは私の実際の状況をより厳密に追跡するサンプルコードです...まだ単純化された例ですが。親コンポーネントは、ユーザーからのコメントを管理します。彼らが新しいコメントを作成し、日付または日付範囲を選択できると想像してみてください。また、既存のコメントを更新することもできます。日付と日付範囲セレクターを独自のコンポーネントに配置しました。したがって、親コメントマネージャコンポーネントには、コメントを作成/ロードし、関連する日付を日付セレクタコンポーネントに渡す機能が必要です。その後、ユーザーは日付を変更できます。これらの値は、後でサーバーに送信して保存するために、親コメントマネージャーに伝播して戻す必要があります。ご覧のとおり、プロパティ値(日付など)の双方向のフローがあり、どちらの端からでもいつでも変更できます。注:この新しい例は、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>
);
}
useEffect
エフェクトをいつ実行するかを示す2番目の引数を取ります。状態が更新されたときに実行されるように、状態値を渡すことができます。また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>
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加