16-7 RN之保存登录状态控制账号模块是否显示用户信息

137 阅读3分钟

课程目标

本节课程的目标是实现以下功能:

  1. 修复错误提示逻辑,避免未点击的输入框显示错误信息。
  2. 在“我的页面”中,根据登录状态动态显示用户信息或登录按钮。
  3. 实现登录状态的持久化,并在应用启动时加载登录信息。
  4. 封装一个高阶组件,用于统一处理登录状态判断和未登录时的显示逻辑。

1. 修复错误提示逻辑

问题

在上一节中,点击任意输入框后,其他输入框也会显示错误提示。

解决方法

Input 组件中,添加对 form.touched[field.name] 的判断,确保只有点击过的输入框才会显示错误信息。

修改后的代码

<Text style={styles.error}>
  {form.touched[field.name] && form.errors[field.name]}
</Text>

2. 动态显示用户信息或登录按钮

目标

在“我的页面”中,根据用户登录状态动态显示:

  • 用户的头像和姓名(已登录)
  • 登录按钮(未登录)

实现步骤

2.1 获取用户信息

dva 仓库中获取 user 对象:

const mapStateToProps = ({login}: RootState) => ({
  user: login.user,
});

const connector = connect(mapStateToProps);

type ModelState = ConnectedProps<typeof connector>;

export default connector(Account);

2.2 动态显示内容

通过判断 user 对象是否存在,动态渲染页面:

const {user} = this.props;

if (user) {
  return (
    <View>
      <View style={styles.loginView}>
        <Image source={{uri: user.avatar}} style={styles.avatar} />
        <View style={styles.right}>
          <Text style={styles.name}>{user.name}</Text>
        </View>
      </View>
      <View style={styles.logoutView}>
        <Touchable onPress={this.logout} style={styles.logoutButton}>
          <Text>退出登录</Text>
        </Touchable>
      </View>
    </View>
  );
}

return (
  <View>
    {/* 未登录时显示的内容 */}
  </View>
);

2.3 样式代码

logoutView: {
  alignItems: 'center',
  marginTop: 200,
},
logoutButton: {
  justifyContent: 'center',
  alignItems: 'center',
  height: 40,
  width: viewportWidth - 40,
  borderRadius: 4,
  borderColor: '#ccc',
  borderWidth: 1,
  marginBottom: 7,
},

2.4 实现登出功能

添加登出按钮的逻辑:

logout = () => {
  const {dispatch} = this.props;
  dispatch({
    type: 'login/logout',
  });
};

3. 实现登录状态的持久化

目标

登录成功后,将用户信息保存到 storage 中,并在应用启动时加载登录信息。

实现步骤

3.1 修改 login 模块

在登录模块中,添加以下内容:

  • 初始状态
const initialState = {
  loging: false,
  user: undefined,
};
  • 保存用户信息到 storage
*login({payload, callback}, {call, put}) {
  const {data} = yield call(axios.post, REQUEST_URL, payload);
  yield put({
    type: 'setState',
    payload: {
      loging: true,
      user: data,
    },
  });
  storage.save({
    key: 'user', // 注意: 请不要在 key 中使用下划线符号!
    data: data,
  });
}
  • 从 storage 加载用户信息
*loadStorage(_, {call, put}) {
  const user = yield call(load, {key: 'user'});
  yield put({
    type: 'setState',
    payload: {
      user: user,
    },
  });
}
  • 登出逻辑
*logout(_, {put}) {
  yield put({
    type: 'setState',
    payload: {
      user: null,
      loging: false,
    },
  });
  storage.save({
    key: 'user',
    data: null,
  });
}
  • 订阅加载用户信息
subscriptions: {
  setup({dispatch}) {
    dispatch({type: 'loadStorage'});
  },
},

4. 封装高阶组件处理登录状态

目标

封装一个高阶组件,用于统一处理登录状态判断和未登录时的显示逻辑。

实现步骤

4.1 创建 Authorized 组件

import React from 'react';
import {View} from 'react-native';

export interface IProps {
  authority: boolean; // 用户是否登录
  noMatch?: () => JSX.Element; // 未登录时显示的组件
}

class Authorized extends React.PureComponent<IProps> {
  render() {
    const {children, authority, noMatch = null} = this.props;

    // 如果已登录,则渲染子组件
    if (authority) {
      return children;
    }

    // 未登录时,渲染 noMatch 函数返回的组件
    return <View>{noMatch && noMatch()}</View>;
  }
}

export default Authorized;

4.2 在“我的页面”中使用

unLogin = () => {
  return (
    <View style={styles.header}>
      <Image source={defaultAvatar} style={styles.image} />
      <View style={styles.headerRight}>
        <Touchable onPress={this.goLogin} style={styles.loginButton}>
          <Text style={styles.loginButtonText}>立即登录</Text>
        </Touchable>
        <Text style={styles.tip}>登录后自动同步所有记录哦~</Text>
      </View>
    </View>
  );
};

<Authorized authority={!!user} noMatch={this.unLogin}>
  <View style={styles.header}>
    <Image source={{uri: user && user.avatar}} style={styles.image} />
    <View style={styles.headerRight}>
      <Text style={styles.loginButtonText}>{user && user.name}</Text>
      <Text style={styles.tip} />
    </View>
  </View>
  <View style={styles.logoutView}>
    <Touchable onPress={this.logout} style={styles.logoutButton}>
      <Text>退出登录</Text>
    </Touchable>
  </View>
</Authorized>

5. 测试功能

  1. 登录后,验证“我的页面”是否正确显示用户信息。
  2. 退出登录,验证是否返回到未登录状态。
  3. 重启应用,验证登录状态是否被正确加载。

6. 小结

6.1 本节内容总结

  1. 修复了错误提示逻辑,避免未点击的输入框显示错误信息。
  2. 实现了动态显示用户信息或登录按钮的功能。
  3. 完成了登录状态的持久化,并在应用启动时加载登录信息。
  4. 封装了 Authorized 高阶组件,用于统一处理登录状态逻辑。

6.2 思想总结

通过封装和状态管理,本节实现了登录状态的可维护性和复用性。这种思想可以应用于其他需要权限控制的场景。


7. 下一节预告

在下一节中,我们将进一步优化登录逻辑,并学习如何处理复杂的权限控制场景。