Reputation: 1454
I recently noticed a very weird behavior of Redux in my app that I would like to share with you. When I click this TouchableOpacity in my settings in order to disconnect, I get the following error:
TypeError: Cannot read property first_name of null
from Homepage.js
So my code is firing an error for that piece component that is in a page where I'm not supposed to go.
Homepage.js
<AppText textStyle={styles.title}>
Welcome {userReducer.infos.first_name}
</AppText>
The workflow that is supposed to happen is the user clicking on the disconnect button, then the tryLogout function is called, which fires the action with the type 'LOGOUT'. Then this action sets to null user related data and information with some additional booleans. The state gets updated then the UI should be re-rendered and componentWillUpdate() called. In there, I check my state to redirect the user to the landing/not connected navigation.
I tried to comment infos: null
in my reducer and my code processes without errors.
This variable is nowhere in my app asking to redirect to any page.
It may seem complicated to debug, since the problem must be deeper than it looks. Anyway, maybe someone has already had a similar problem so I give you here the potential elements to try to solve the problem.
Settings.js
import { tryLogout } from '@actions/user'
const mapDispatchToProps = dispatch => ({
tryLogout: () => dispatch(tryLogout()),
})
class Settings extends React.Component<Props, State> {
// What should normally happen
componentDidUpdate(prevProps: Props) {
const { userReducer, navigation } = this.props
if (
prevProps.userReducer.isSignedUp !== userReducer.isSignedUp ||
prevProps.userReducer.isLoggedIn !== userReducer.isLoggedIn
) {
if (!userReducer.isSignedUp && !userReducer.isLoggedIn) {
navigation.navigate('Landing')
}
}
}
// Inside my render() method
<AppTouchableOpacity
onPress={() => tryLogout()}
style={styles.touchableOpacity}
>
Disconnect
</AppTouchableOpacity>
}
actions/user.js
export const tryLogout = (): Function => (dispatch: Function) => {
const logout = () => ({
type: LOGOUT,
})
dispatch(logout())
}
reducers/user.js
case LOGOUT:
return {
...state,
data: null,
infos: null,
isLoggedIn: false,
isSignedUp: false,
}
App.js (I'm using React Navigation)
const LandingScenes = {
Welcome: { screen: WelcomeScreen },
{ /* Some other screens */ }
}
const LandingNavigator = createStackNavigator(LandingScenes, {
initialRouteName: 'Welcome',
defaultNavigationOptions: {
header: null,
},
})
const SecuredScenes = {
Homepage: { screen: HomepageScreen },
Settings: { screen: SettingsScreen },
{ /* Some other screens */ }
}
const SecuredNavigator = createStackNavigator(SecuredScenes, {
initialRouteName: 'Homepage',
defaultNavigationOptions: {
header: null,
},
})
export default createAppContainer(
createSwitchNavigator(
{
Landing: LandingNavigator,
Secured: SecuredNavigator,
},
{
initialRouteName: 'Landing',
}
)
)
Upvotes: 1
Views: 71
Reputation: 6512
Sounds like it's trying to render, when the value of 'infos' is null.
This makes sense, as the homescreen is still living in main memory, in your navigation stack, this is because you're using the 'navigate' method when navigating to settings.
So this is a result of using react-navigation, not redux, or redux thunk.
To handle this error case in your view, you could not render the text unless infos is defined. Like below as an example, you could of course display a loader, or whatever if infos is undefined/null, instead of nothing, or even a default message.
Really, the final answer here is restructuring your navigation. You could look at using a different method to navigate to the settings page, which resets the stack navigator, or keep it handled in the view, both is the ideal.
Look at the replace method for instance, this is used to reset the navigation stack, or even look at the switch navigator, which is made to only show one screen (where a screen can be a stack navigator, i.e. many screens) at a time, creating a separation between contexts within your app.
{userReducer.infos && (
<AppText textStyle={styles.title}>
Welcome {userReducer.infos.first_name}
</AppText>
)}
Upvotes: 1
Reputation: 2222
If I understand correctly, the issue is that the Homepage
component isn't expected to be loaded when the user signs out, but is throwing a null
error on infos
. Since you are using a stack navigator it is likely that your Homepage component is actually still mounted in the stack. You will need to either handle the null
case in your Homepage
or remove it from the stack during navigation to Settings
.
You can find more details about the lifecycle in react-navigation in their documentation here: Navigation lifecycle - Example scenario, the important part being here:
We start on the HomeScreen and navigate to DetailsScreen. Then we use the tab bar to switch to the SettingsScreen and navigate to ProfileScreen. After this sequence of operations is done, all 4 of the screens are mounted!
Upvotes: 1