【问题标题】:react native render view does not update after state changes auth component状态更改身份验证组件后反应本机渲染视图不更新
【发布时间】:2018-09-11 12:24:46
【问题描述】:

大家好

我是这个 react-native 的新手,我正在尝试让 react-native、react router 4 和 firebase auth 工作。我要做的是检查用户是否已登录。如果他们已登录,它将显示“splash”组件,如果未登录,它将重定向到“/login”屏幕。

我确实遵循了react router 4 auth 文档,但我就是无法让它工作。现在我确实想知道我在这里缺少什么,是否有人可以向我解释一下,以便我理解为什么它不起作用。

这是我想要实现的流程:

  1. 用户可以在未授权时导航到“/login”和“/register”
  2. 如果未经身份验证,用户无法导航到“/”
  3. 当用户通过身份验证后,他们将转到“/”并且组件“splash”附加到该路径
  4. 组件“splash”具有受 HOC 保护的嵌套路由

我正在为我的项目使用 create-react-native-app,这是我的文件。

app.js(根组件)

export default class App extends React.Component {
constructor() {
    super();
    this.state = {
        isAuth: false,
        email: "",
        password: ""
    };
}

onHandleLogin = (e, type) => {
    const event = e.nativeEvent.text;
    this.setState({ [type]: event });
};

onSubmitLogin = () => {
    const email = this.state.email;
    const password = this.state.password;
    firebase
        .auth()
        .signInWithEmailAndPassword(email, password)

        .catch(err => {
            console.log("error:", err);
        });
};

isLoggedIn = () => {
    firebase.auth().onAuthStateChanged(user => {
        if (user) {
            this.setState({ isAuth: true });
            return true;
        } else {
            this.setState({ isAuth: false });
            return false;
        }
    });
};

componentDidMount() {
    this.isLoggedIn();
}

render() {
    const PrivateRoute = ({ component: Component, ...rest }) => (
        <Route
            {...rest}
            render={props =>
                this.isLoggedIn() ? (
                    <Component {...props} />
                ) : (
                    <Redirect
                        to={{
                            pathname: "/login",
                            state: { from: props.location }
                        }}
                    />
                )
            }
        />
    );
    return (
        <NativeRouter>
            <View style={styles.container}>
                <Switch>
                    <Route
                        path="/login"
                        render={props => (
                            <Login
                                {...props}
                                submitFunc={this.onSubmitLogin}
                                login={this.onHandleLogin}
                                msg={this.state.isAuth}
                            />
                        )}
                    />
                    <Route path="/register" component={Register} />
                    <PrivateRoute path="/" component={Splash} />
                </Switch>
            </View>
        </NativeRouter>
    );
}
}

Splash.js

import Home from "./../Home/Home";

const Splash = () => {
    return (
        <View style={styles.container}>
            <Route exact path="/" component={Home} />
        </View>
    );
};

Login.js

const Login = ({ login, submitFunc }) => {
return (
    <KeyboardAwareScrollView
        resetScrollToCoords={{ x: 0, y: 0 }}
        contentContainerStyle={styles.formContainer}
    >
        <InputContainer style={styles.inputContainer}>
            <Logo source={logo} />
            <Input
                inputStyle={styles.input2}
                onChange={e => login(e, "email")}
                placeholder="Email"
                clearButtonMode="always"
                containerStyle={styles.input}
                leftIcon={
                    <Icon
                        containerStyle={styles.iconContainer}
                        name="at"
                        size={20}
                        type="font-awesome"
                        color="#b2b2b2"
                    />
                }
            />
            <Input
                inputStyle={styles.input2}
                onChange={e => login(e, "password")}
                placeholder="Password"
                clearButtonMode="always"
                containerStyle={styles.input}
                secureTextEntry={true}
                leftIcon={
                    <Icon
                        containerStyle={styles.iconContainer}
                        name="lock"
                        size={20}
                        type="font-awesome"
                        color="#b2b2b2"
                    />
                }
            />
            <Button
                title="Log in"
                buttonStyle={styles.button}
                titleStyle={styles.buttonTitle}
                onPress={() => submitFunc()}
            />
            <Button
                title="Forgot your password?"
                buttonStyle={styles.buttonLink}
                titleStyle={styles.buttonLinkTitle}
            />
            <ForgotContainer>
                <Text style={styles.text}>Don't have an account? </Text>
                <Link component={TouchableOpacity} to="/register">
                    <Text>Create one</Text>
                </Link>
            </ForgotContainer>
        </InputContainer>
    </KeyboardAwareScrollView>
);
};

感谢所有我能得到的帮助和解释,谢谢你们提前

【问题讨论】:

    标签: javascript reactjs react-native react-router firebase-authentication


    【解决方案1】:

    根据火力库documentationonAuthStateChanged添加了一个监听器/观察者并返回一个非空函数():观察者的取消订阅函数。

    onAuthStateChanged(nextOrObserver, error, completed) returns function()

    查看您的代码,您做错了两件事

    1. PrivateRoute 组件处再次重新初始化侦听器,但什么都不做。
    2. 不是unsubscribing 侦听器,这反过来又会增加额外的开销,只要整个应用的身份验证状态发生变化

    最佳方法是如下进行

    componentDidMount() {
        // Bind the variable to the instance of the class.
        this.authFirebaseListener = firebase.auth().onAuthStateChanged((user) => {
          this.setState({
            loading: false,  // For the loader maybe
            user, // User Details
            isAuth: true
          });
        });
    
      }
    
    componentWillUnmount() {
       this.authFirebaseListener && this.authFirebaseListener() // Unlisten it by calling it as a function
    }
    

    在您的PrivateRoute 组件中,使用this.state 将其注入您的组件(如果您希望您的组件是独立的,或者直接注入)

    const PrivateRoute = ({ component: Component, isAuth, ...rest }) => ( // Pass it as a prop where your component is injected
            <Route
                {...rest}
                render={props =>
                    isAuth ? (
                        <Component {...props} />
                    ) : (
                        <Redirect
                            to={{
                                pathname: "/login",
                                state: { from: props.location }
                            }}
                        />
                    )
                }
            />
        );
    

    <PrivateRoute path="/" isAuth={this.state.isAuth} component={Splash} />
    

    【讨论】:

    • 感谢您向我解释这一点。我现在明白这是如何连接的
    • stackoverflow 说我可以在 21 小时内奖励你 150 赏金,让你知道
    【解决方案2】:

    不确定这是否是问题,但 this.isLoggedIn 没有调用函数

    this.isLoggedIn() 实际上是在调用一个函数

    【讨论】:

    • 我看到的一件事是,您不会等到知道结果后才知道结果,这意味着您的组件将重定向您需要一个状态变量,比如 firebaseFetched 或类似的,并在渲染中执行 if (!this .state.firebaseFetched) 返回“正在加载...”;并在您的 onAuthStateChanged this.setState({firebaseFetched:true 旁边设置 loggedIn
    【解决方案3】:

    您应该在 App 渲染函数中检查 this.state.isAuth 而不是 this.isLoggedIn()

    this.isLoggedIn 不返回任何内容,并且始终是虚假的(未定义),因此您将始终转到重定向案例。

    【讨论】:

      猜你喜欢
      • 2017-03-24
      • 2020-10-07
      • 2022-11-17
      • 2022-07-08
      • 2021-01-27
      • 2019-04-03
      • 2018-11-06
      • 2021-06-20
      • 2022-07-12
      相关资源
      最近更新 更多