使用反应挂钩时如何通知父组件属性更改?

时间:2019-01-29 17:55:07

标签: reactjs react-hooks

可以说我有一个父组件和一个子组件。父组件由几个子组件组成。父组件保存并管理一个非常复杂且深层的数据对象。每个子组件都提供用于管理各种子对象和主数据对象属性的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>
    );
}

1 个答案:

答案 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>