在启动时,我使用useEffect
从本地存储中获取一些数据。如果获取成功,我想发送一个动作并更改组件的状态。分派动作后,我看到减速器收到了一个动作并返回了新状态,但是当我尝试记录新状态时,我没有看到组件的任何更改。出现这种行为的原因可能是什么。
这里是action.ts
:
import {TOKEN_VALIDITY} from './actionTypes'
export const setTokenValidity = (isTokenValid: boolean) => ({
type: TOKEN_VALIDITY,
isTokenValid
})
这里是reducer.ts
:
const auth = (state = false, action: any) => {
// after dispatch I see this log in the console value is true
console.log('auth reducer action type is ' + action.type + ' value is ' + action.isTokenValid)
switch(action.type) {
case TOKEN_VALIDITY:
return action.isTokenValid
default:
return state
}
}
export default auth
在组件中,我想更新isTokenValid的状态,但是我总是无法确定其值。
这是组件代码
const Stack = createStackNavigator();
let userToken = null;
const App = (props:any) => {
useEffect(() => {
const bootstrapAsync = async () => {
try {
userToken = await retrieveData('Token',null);
SplashScreen.hide();
if(userToken != null) {
props.setTokenValidity(true)
}
// this logs undefined for props.isTokenValid -- why???
console.log("after token isValid: " + props.isTokenValid)
console.log('token ' + userToken);
} catch (e) {
// Restoring token failed
}
};
bootstrapAsync();
}, []);
return (
<NavigationContainer>
<Stack.Navigator >
{!props.isTokenValid ? (
<>
<Stack.Screen name="Login" component={Login} options={{ headerShown:false }}/>
<Stack.Screen name="Home" component={Home} options={{ headerShown:false }}/>
</>
) : (
<Stack.Screen name="Home" component={Home} options={{ headerShown:false }}/>
)}
</Stack.Navigator >
</NavigationContainer>
);
};
const mapDispatchToProps = (dispatch:any) => ({
setTokenValidity: (isTokenValid:boolean) => dispatch(setTokenValidity(isTokenValid))
})
const mapStateToProps = (state:any) => ({
isTokenValid: state.isTokenValid
})
export default connect(mapStateToProps, mapDispatchToProps)(App);
答案 0 :(得分:1)
在第一个渲染中,即触发useEffect
并使用props.setTokenValidity
调用该方法来设置令牌有效性时,令牌就被设置了。但是,控制台也会在相同的渲染图上打印。
当状态得到更新并且您使用props.isTokenValid
获得更新后的状态时,这是第二次重新渲染(调用useEffect时是不同的渲染),并且useEffect
不会触发,因此我们看不到控制台打印有新值。
如果出于某种原因要在设置isTokenValid
时登录,请使用另一个useEffect
useEffect(() => {
console.log("after token isValid: " + props.isTokenValid)
},[props.isTokenValid]);
答案 1 :(得分:0)
Reducer返回一个状态对象,因此您可能想尝试执行以下操作:
switch(action.type) {
case TOKEN_VALIDITY:
return { ...state, isTokenValid: action.isTokenValid }
default:
return state
}
答案 2 :(得分:0)
我在项目中解决该问题的方法是创建多个useEffects。
要解决您的问题,您需要做两件事:
bootstrapAsync()
userToken
中的数据您已经成功地完成了1号操作,并且当前的问题是您的组件在接收到新数据(也就是userToken
更新时)没有更新。
解决方案:
编写另一个useEffect函数,该函数将呈现2次:一次在组件加载时(由于尚未完成获取,我们将忽略此操作),而另一次在userToken
值更新时进行。
为了避免在组件加载时运行新的useEffect,我们需要创建一个新状态,我们将其称为allowNavigation。
allowNavigation
属性仅在获取完成后才会设置为true。
然后,仅当allowNavigation设置为true
时,我们才能检查userToken
并正确处理。
以下代码将为您提供帮助:
const App = (props:any) => {
const [allowNavigate, setAllowNavigate] = useState(false);
useEffect(() => {
const bootstrapAsync = async () => {
try {
userToken = await retrieveData('Token',null);
SplashScreen.hide();
setAllowNavigate(true);
...
};
bootstrapAsync();
}, []);
useEffect(() => {
if (allowNavigate)
// this will now log the correct values
console.log("after token isValid: " + props.isTokenValid)
console.log('token ' + userToken);
}, [allowNavigate]);
...