如何在 React 的子功能组件中触发一个动作?

How does one trigger an action in a child functional component in React?

使用基本的 form/input 布局,很明显回调应该用于从子组件到父组件的状态更改(由子组件发起),但是父组件如何要求子组件重新评估其陈述并传达给家长?

这里的最终目标只是在提交表单按钮时触发对子输入的验证。

给出的 [ts] 代码如下所示:

    const Login : React.FC<Props> = (props) => {
        ...useStates omitted

        const onSubmit = () : void => {
          //trigger `verify()` in PasswordInput to get up-to-date `valid` state var

 
        }
        
        return (
            <PasswordInput
              onValidChange={setValid} />
            <Button
              onPress={submit} />
        )
    }


    const PasswordInput : React.FC<Props> = (props) => {
        ...useStates omitted

        const verify = () => {
          //verify the password value from state

          props.onValidChange(true)
        }


        return (<Input onBlur={verify}/>) 
    }

Notes/paths 到目前为止:

更新 经验教训:

一个简单的例子来说明如何解决这个问题

function Child(props)
{
    const validate=()=> alert('hi from the child');
    props.registerCallback(validate)
    return (<div>I'm the child</div>)
}

function Parent()
{
    const callbackRef = React.useRef();
    function registerCallback(callback)
    {
        callbackRef.current = callback;
    }
    return (<div><Child  registerCallback={registerCallback}/>
        <button onClick={() => callbackRef.current()}>say hello</button></div>)
}

https://jsfiddle.net/4howanL2/5/

在进一步了解 React 和工作解决方案的几次迭代后,我决定使用 Reducer 来完成此任务。

我没有将子组件视为要调用的函数,而是不得不改变我的想法,更多地围绕更新状态并相信子组件能够正确表示该状态。对于我原来的例子,我最终构建了类似这样的东西(一切都简化和修剪):

interface LoginState {
    email: { 
      status: 'none' | 'verify' | 'valid', 
      value?: string
    }
    password: { 
      status: 'none' | 'verify' | 'valid', 
      value?: string
    }
    submit: boolean
}
const Login : React.FC<Props> = (props) => {

        const [state, dispatch] = useReducer<Reducer>(reducer, {
            email: { status: 'none' },
            password: { status: 'none' }}
        })

        export const reducer = (state : LoginState, change : StateChange) : LoginState => {

            if (change.email) {
                state.email = _.merge({}, state.email, change.email)
            }

            if (change.password) {
                state.password = _.merge({}, state.password, change.password)
            }

            return _.merge({}, state)
        }

        const submit = () : void => {
            dispatch({
                email: { status: 'verify' }},
                password: { status: 'verify'}}},
                submit: true
            })
 
        }

        useEffect(() => {
            if (!state.submit 
                || state.email.status == 'none' 
                || state.password.satus == 'none') {
                return
            }

            //ready to submit, abort if not valid
            if (state.email.status == 'invalid'
                || state.password.status == 'invalid') {
                dispatch({ submit: false})
                return
            }

           //all is valid after this point
        }, [state.email, state.password])
        
        return (
            <Input ...
            <PasswordInput
              state={state.password}
              dispatch={dispatch} />
            <Button
              onPress={submit} />
        )
    }


    const PasswordInput : React.FC<Props> = (props) => {

        //setup onChangeText to dispatch to update value

        return (<Input
                  //do something visual with props.state.status
                  value={props.state.value}
                  onBlur={dispatch({ status: 'verify'})} ... />) 
    }

以上是粗略的代码,但要点在那里。这传达了基础价值的更新状态,这些价值在中央层面被减少。然后通过告诉输入它们处于状态 verify,在子组件级别向下呈现此状态。当 submit 设置为 true 时,我们尝试提交表单,并在此过程中进行验证。

我使用 EventEmitter 完成了类似的事情。它类似于已经描述的 registerCallback 方法,但恕我直言,它更简洁一些。

    function Child({eventEmitter})
    {
        eventEmitter.on('sayHello', () => alert('hi from the child'))
        return (<div>I'm the child</div>)
    }

    function Parent()
    {
        const eventEmitter = new EventEmitter()
        return (
            <div>
                <Child eventEmitter={eventEmitter}/>
                <button onClick={() => eventEmitter.emit('sayHello')}>
                    say hello
                </button>
            </div>
        )
    }