React:如何在渲染组件之前初始化Redux状态?

时间:2020-05-24 17:37:16

标签: react-native redux

我正在尝试使用Redux创建具有用户登录功能的基本应用程序来管理用户详细信息。我已经在下面链接了我的屏幕的GIF,并且如您所见,在加载组件和用户详细信息渲染之间存在延迟。还记录了我的组件配置文件代码。

Name of user delay when loading

import React, {Component} from 'react';
import {View, StyleSheet, Text, TouchableOpacity} from 'react-native';
import {connect} from 'react-redux';
import {fetchProfile} from '../../actions/ProfileActions';
import {logoutUser} from '../../actions/AuthActions';

class Profile extends Component {
  state = {
    firstName: '',
    lastName: '',
    email: '',
    goals: '',
  };

  componentDidMount() {
    this.props.fetchProfile();
  }

  componentDidUpdate(prevProps) {
    if (prevProps !== this.props) {
      this.setState({
        firstName: this.props.profile.firstName,
        lastName: this.props.profile.lastName,
        email: this.props.profile.email,
        goals: this.props.profile.goals,
      });
    }
  }

  onPressLogout = () => {
    this.props.logoutUser();
  };

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.headerContainer}>
          <Text style={styles.header}>
            Profile of {this.state.firstName} {this.state.lastName}
          </Text>
        </View>
        <View style={styles.textContainer}>
          <TouchableOpacity
            style={styles.buttonContainer}
            onPress={this.onPressLogout.bind(this)}>
            <Text style={styles.buttonText}>Logout</Text>
          </TouchableOpacity>
        </View>
      </View>
    );
  }
}

const mapStateToProps = (state) => ({
  profile: state.profile.profile,
});

export default connect(mapStateToProps, {fetchProfile, logoutUser})(Profile);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F9F9F9',
  },
  headerContainer: {
    marginTop: 75,
    marginLeft: 20,
  },
  header: {
    fontSize: 34,
    fontWeight: '700',
    color: '#000000',
  },
  textContainer: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    marginBottom: 30,
  },
  buttonContainer: {
    backgroundColor: '#34495E',
    alignItems: 'center',
    padding: 12,
    width: 350,
    borderRadius: 15,
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold',
    fontSize: 19,
  },
});

编辑:我忘了解释fetchProfile()的作用。它连接到Firebase数据库以检索用户的详细信息。下面的代码:

import {PROFILE_FETCH} from './types';
import firebase from 'firebase';

export const fetchProfile = () => {
  const {currentUser} = firebase.auth();

  return (dispatch) => {
    firebase
      .database()
      .ref(`/users/${currentUser.uid}/profile`)
      .on('value', (snapshot) => {
        dispatch({
          type: PROFILE_FETCH,
          payload: snapshot.val(),
        });
      });
  };
};

此外,我在应用程序中有3个不同的屏幕,所有这些屏幕都可能会利用用户的详细信息。我敢肯定,必须有一种更有效的方法,只需一次fetchProfile(),然后以某种方式将详细信息传递给每个组件...

我该怎么办,以便用户登录时,其详细信息已经加载到组件中,因此没有延迟?预先感谢!

3 个答案:

答案 0 :(得分:0)

解决这个问题的一种方法是有条件地渲染skeleton(如果它仍在加载),然后在完成后实际渲染细节。 我不确定这是否正是您要寻找的解决方案(您可能已经知道这是一个选择),但这也许有帮助吗?

答案 1 :(得分:0)

根据您提供的内容,肯定会有延迟。我将在这里说明发生了什么。

在个人资料上呈现详细信息后,您正在从Firebase请求数据。发生这种情况是因为您要在componentDidMount方法中请求数据。首次调用此方法时,Render方法完全完成了组件的渲染。因此,我建议您使用两种方法来消除这种情况。

  1. 按照Coding Duck的建议,您可以显示一个骨架加载器,直到从firebase中获取数据为止。
  2. 您可以从登录名中请求这些数据。这意味着,如果用户身份验证成功,则可以使用fetchProfile操作请求这些数据,一旦完全获取了这些数据,就可以使用Navigation.navigate('Profile')导航到“个人资料”屏幕,而不是直接导航到该屏幕认证成功。在那段时间内,因为您已经获取了数据,所以不会有任何问题。

此外,您还可以使用firebasepersist选项在本地存储这些数据。因此,即使没有互联网连接,firebase仍会迅速提供您的个人资料信息。

编辑

更具体的答案,带有一些随机的类和函数名称。这只是一个例子。

让我们说onLogin函数处理您的身份验证类中的所有登录要求。

onLogin = () => {
  /** Do the validation and let's assume validation is success */
  /** Now you are not directly navigating to your Profile page like you did in the GIF. I assume that's what you did because you have not added more code samples to fully understand what you have done.*/
  /** So now you are calling the fetchProfile action through props and retrieve your details. */
  this.props.fetchProfile(this.props.navigation);
};

现在让我们修改您的fetchDetails操作。

export const fetchProfile = (navigation) => {
  const {currentUser} = firebase.auth();

  return (dispatch) => {
    firebase
      .database()
      .ref(`/users/${currentUser.uid}/profile`)
      .on('value', (snapshot) => {
        dispatch({
          type: PROFILE_FETCH,
          payload: snapshot.val(),
        });
        navigation.navigate('Profile')
      });
  };
};

注意:这不是处理导航的最佳方法,而是使用全局导航服务直接访问顶级导航器。您可以在React Navigation Documentation中进一步了解。但是,在此示例中,我们现在就使用它。

如您所见,当用户登录成功时,现在您不需要在渲染“个人档案”页面后请求数据,而是在导航到该页面之前就请求数据。因此,这可以确保配置文件页面仅加载相关数据,并且不会像GIF中那样出现延迟。

答案 2 :(得分:0)

您必须使用Firebase创建一个侦听器。

做这样的事情:

减速器动作:

// Action Creators
export function onAuthChanged(fb) {
  return async (dispatch) => {
    fb.auth().onAuthStateChanged((res) => {
      dispatch({
        type: Types.SET_ATTR,
        payload: {
          attr: 'user',
          value: res,
        },
      });
    });
  };
}

从FirebaseProvider componentWillMount函数调用此函数

然后

将FirebaseProvider放在您的App类上;

const App = () => {
  return (
    <>
      <Provider store={store}>
        <TranslatorProvider />
        <FirebaseProvider />
        <ThemeProvider>
          <TrackingProvider>
            <Routes />
          </TrackingProvider>
        </ThemeProvider>
      </Provider>
    </>
  );
};

监听器将在登录和注销时将用户保存在您的reducer上