【问题标题】:How to access updated props in recaptcha callback如何在 recaptcha 回调中访问更新的道具
【发布时间】:2019-11-20 15:54:57
【问题描述】:

我正在尝试通过 React 和 Redux 使用集成在 firebase 身份验证中的 reCaptcha。

const Login = props => {

    useEffect(() => {
        window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('login-button', {
                'size': 'invisible',
                'callback': () => login()
            }
        );
        window.recaptchaVerifier.render()
    }, []);

    function login() {
        console.log(props)
        firebaseLogin(props.email, props.password).then(() => {
                ...
            }
        ).catch(function (error) {
            console.log(error);
            ...
        });
    }

    function onChangePassword(event) {
        props.onChangePassword(event.target.value)
    }

    function onChangeEmail(event) {
        props.onChangeEmail(event.target.value)
    }

    return (
        ...
            <form>
                <label>Dirección de Email</label>

                <div className="form-group">
                    <input type="email"
                           className={`form-control ${emailValidation ? "is-invalid" : ""}`}
                           placeholder="nombre@mail.com"
                           onChange={onChangeEmail} value={props.email} tabIndex={1}
                           autoComplete={"username"}
                           required/>
                    <div className="invalid-feedback">
                        {emailValidation}
                    </div>

                </div>

                <div className="form-group">

                    <div className="row">
                        <div className="col">

                            <label>Contraseña</label>

                        </div>
                        <div className="col-auto">

                            <Link to="/reset-password" className="form-text small text-muted">
                                Olvidé mi contraseña
                            </Link>

                        </div>
                    </div>

                    <div className="input-group input-group-merge">

                        <input type={showPassword ? "text" : "password"}
                               className={`form-control ${passwordValidation ? "is-invalid" : ""} form-control-appended`}
                               placeholder="Ingrese su contraseña" onChange={onChangePassword}
                               value={props.password} required tabIndex={2}
                               autoComplete={"current-password"}/>

                        <div className="input-group-append">
                      <span className="input-group-text clickable"
                            onClick={() => setShowPassword(!showPassword)}>
                        <i className="fe fe-eye"/>
                      </span>
                        </div>
                        <div className="invalid-feedback">
                            {passwordValidation}
                        </div>

                    </div>
                </div>

                <button type="submit" className="btn btn-lg btn-block btn-primary mb-3"
                        id={"login-button"}
                        tabIndex={3}>
                    Acceder
                </button>
            </form>
        ...
    )
};

const mapDispatchToProps = dispatch => ({
    onChangeEmail: value =>
        dispatch({type: UPDATE_LOGIN_FIELD, key: 'formEmail', value}),
    onChangePassword: value =>
        dispatch({type: UPDATE_LOGIN_FIELD, key: 'password', value}),
});

const mapStateToProps = state => ({
    email: state.authentication.formEmail,
    password: state.authentication.password,
});

export default connect(mapStateToProps, mapDispatchToProps)(Login);

问题在于,在登录回调中,道具已过时,并且不包含在 redux 存储中加载的电子邮件和密码,它仅包含触发 useEffect 时可用的道具。 (我通过 console.log(props) 看到了)

我不知道如何保留对更新道具的引用。我试过使用 useRef 钩子并将其作为参数发送到登录,但这也不起作用。

我也尝试使用 email 和 password 属性作为 useEffect 依赖项,但这会触发 recaptcha 生成很多次,而且效率很低。

感谢您的帮助!

【问题讨论】:

    标签: javascript reactjs redux firebase-authentication recaptcha


    【解决方案1】:

    似乎工作

    window.login = useCallback(() => {
            console.log(props);
            firebaseLogin(props.email, props.password).then(() => {
                 ...
                }
            ).catch(function (error) {
                console.log(error);
                ....
                }
        }, [props]);
    
    useEffect(() => {
            window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('login-button', {
                    'size': 'invisible',
                    'callback': () => window.login(),
                }
            );
            window.recaptchaVerifier.render();
        }, []);
    

    还有一个问题,但与本题无关

    【讨论】:

    • 考虑也许还向useEffect 添加一个return 语句,例如:return window.login = undefined 以在卸载组件时清除窗口对象,或者正如我在下面的评论中提到的,而是将函数登录绑定到组件的 ref 而不是 window.
    【解决方案2】:

    使用回调挂钩以高效的方式访问最新状态。

        const login = useCallback( () => {
            console.log(props)
            firebaseLogin(props.email, props.password).then(() => {
                    ...
                }
            ).catch(function (error) {
                console.log(error);
                ...
            });
        }, [ props ] ):
    

    在官方 React 文档中,它声明:“传递一个内联回调和一组依赖项。useCallback 将返回回调的记忆版本,只有当其中一个依赖项发生更改时才会更改。”在我们的例子中,登录函数会在每次[props] 更改时自行更新,这意味着当我们获得新的道具时,我们会获得一个新的登录函数。

    查看官方hook documentation了解更多关于如何使用哪个钩子的信息。

    您甚至可以将useCallback 用于所有三个功能!

        const onChangePassword = useCallback( (event) => {
            props.onChangePassword(event.target.value)
        }, [props.onChangePassword]);
    
        const onChangeEmail = useCallback( (event) => {
            props.onChangeEmail(event.target.value)
        }, [props.onChangeEmail]);
    

    每当您的 props.onChangePasswordprops.onChangeEmail 函数从 redux 或高阶组件更新时,onChangePasswordonChangeEmail 回调函数都会更新并创建新函数。

    【讨论】:

    • 如果我将 useCallback 与 props 设置为依赖项,那么我应该将“login”作为 useEffect 的依赖项,这最终会导致同样的问题,在每次输入更新时都会调用 recaptcha。无论如何,在这样做的同时,道具仍然具有相同的信息。我认为登录按钮中的事件仍然引用旧回调,即使我之前做了一个 window.recaptchaVerifier.clear()。
    • 我明白你的意思,你自己对问题的回答似乎可以解决问题,但是我宁愿尝试将登录功能绑定到组件的引用而不是窗口。
    • 您知道如何进行绑定吗?考虑到 recaptcha 验证器已经在窗口中,我不确定其他方法是否可行
    • 它适用于window,因为window 是一个可以从任何地方访问的全局对象。 window.login 将设置为login 的最新功能(请参阅您的答案,在useCallback 中您更新window.login)。如果您改为访问 ref.login ,情况也是如此。唯一的区别是您不使用窗口,而是使用范围更好的全局对象。 const ref = useRef(null);。当你的组件被卸载时 Ref 将被销毁。
    • 我不确定您需要如何应用 useRef 挂钩。你的意思是做类似 const loginRef = useRef(login) 然后在 useEffect 中调用 loginRef 吗?
    猜你喜欢
    • 1970-01-01
    • 2021-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    相关资源
    最近更新 更多