Как уведомить родительский компонент об изменении свойства при использовании реакционных хуков? - PullRequest
0 голосов
/ 29 января 2019

Допустим, у меня есть родительский компонент и дочерний компонент.Родительский компонент состоит из нескольких дочерних компонентов.Родительский компонент содержит и управляет очень сложным и глубоким объектом данных.Каждый дочерний компонент предоставляет пользовательский интерфейс для управления различными дочерними объектами и свойствами основного объекта данных.Всякий раз, когда дочерний компонент изменяет значение свойства в иерархии объекта данных, это изменение должно доходить до основного объекта данных.

Вот как я могу сделать это в классе дочернего компонента, передав объект обратного вызова...

<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'sответ и отзыв Райана.Шубхэм ответила на вопрос в ответ на вопрос, но Райан предлагает мне привести более подробный пример, чтобы убедиться, что я даю правильную информацию для правильного ответа.Вот пример кода, который более точно следует моей реальной ситуации ... хотя все еще является упрощенным примером.Родительский компонент управляет комментариями пользователей.Представьте, что они могут создавать новые комментарии и выбирать дату или диапазон дат.Они также могут обновить существующие комментарии.Я поместил селектор даты и диапазона дат в его собственный компонент.Поэтому родительский компонент диспетчера комментариев должен иметь возможность создавать / загружать комментарии и передавать связанные даты в компонент выбора даты.Затем пользователь может изменить дату (даты), и эти значения должны быть переданы обратно в родительский менеджер комментариев для последующей отправки на сервер и сохранения.Итак, вы видите, что существует двунаправленный поток значений свойств (даты и т. Д.), Которые могут быть изменены в любое время с любого конца.ПРИМЕЧАНИЕ. Этот новый пример обновляется с использованием метода, аналогичного предложенному Шубхэмом, на основе моего исходного вопроса.

==================================================================

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 голосов
/ 29 января 2019

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>
...