课程目标
本节课程的目标是实现以下功能:
- 修复错误提示逻辑,避免未点击的输入框显示错误信息。
- 在“我的页面”中,根据登录状态动态显示用户信息或登录按钮。
- 实现登录状态的持久化,并在应用启动时加载登录信息。
- 封装一个高阶组件,用于统一处理登录状态判断和未登录时的显示逻辑。
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. 测试功能
- 登录后,验证“我的页面”是否正确显示用户信息。
- 退出登录,验证是否返回到未登录状态。
- 重启应用,验证登录状态是否被正确加载。
6. 小结
6.1 本节内容总结
- 修复了错误提示逻辑,避免未点击的输入框显示错误信息。
- 实现了动态显示用户信息或登录按钮的功能。
- 完成了登录状态的持久化,并在应用启动时加载登录信息。
- 封装了
Authorized高阶组件,用于统一处理登录状态逻辑。
6.2 思想总结
通过封装和状态管理,本节实现了登录状态的可维护性和复用性。这种思想可以应用于其他需要权限控制的场景。
7. 下一节预告
在下一节中,我们将进一步优化登录逻辑,并学习如何处理复杂的权限控制场景。