how to call a function on page refresh in react-redux? - api

Iam doing an app in react-redux and i want to hold my redux store on page refresh and thought to not make use of predefined libraries and hence i set the redux state to local state and making the api call in componentWillUnmount to restore redux state on page refresh.But i couldnt do that. Is their any approch to acheive this:
And my code is:
componentWillMount(){
this.setState({
activeUser:this.props.activeUser
})
}
componentWillUnmount(){
this.props.loginUser(this.state.activeUser.user);
}
activeUser is my redux state and this.props.loginUser() makes api call.And i tried of using event handlers as:
componentDidMount(){
window.addEventListener('onbeforeunload', this.saveStore)
}
componentWillUnmount(){
window.removeEventListener('beforeunload', this.saveStore)
}
saveStore(){
this.props.loginUser(this.state.activeUser.user);
}
but it didn't worked for me.

My understanding is that what you basically are trying to do is that, you want to persist your app state (user info, etc) between reloads.
One can use the localStorage API to achieve this effect.
I'll give one possible solution down here.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {activeUser: null};
}
componentWillMount() {
let activeUser = localStrorage.getItem("activeUser");
if (activeUser) {
this.props.receivedActiveUser(JSON.parse(activeUser));
}
}
componentWillReceiveProps(nextProps) {
this.setState({activeUser: nextProps.activeUser});
}
componentWillUnmount(){
if (this.state.activeUser) {
localStorage.setItem("activeUser", JSON.stringify(this.state.activeUser));
}
}
}
Ofcourse, you'll have to create a receivedActiveUser action which will update the store appropriately.

Related

React Native: How can I redirect after login to different pages depending on the type of account of a user?

I'm building a react native app using expo and I would like to know how I can send a "UserTypeA" to Homepage and send a "UserTypeB" to Profile upon login.
I have a UserTypeA tab navigator and a UserTypeB tab navigator, with just 2 pages that will be see able by both accounts.
I have my UserTypeA data and UserTypeB data in separate tables so I can identify which user has which type.
Sorry if it's not clear this is my first question.
Thank you for your help!
In your apps main render method, you could do something like this.
Basically, you will listen to your redux state and switch main screen depending on the user type.
class MyApp extends PureComponent {
constructor(props) {
super(props);
}
render() {
const { auth } = this.props;
if (auth.userObj.type1) {
return <Type1MainComponent />;
}
if (auth.userObj.type2) {
return <Type2MainComponent />;
}
return <LoginScreen />;
}
}
function mapStateToProps(state) {
const { auth } = state;
return { auth };
}
export default connect(mapStateToProps)(MyApp);

Changing state in React native App.js from another component

I'm making authentication in an app, and I'm kind of stuck. I have 2 different navigations. One shows if the user is logged in and another one if not. Basically, a Sign in screen. It's working fine if I change the value manually upon the start. But I can't find a way to change a state when a user signs in, for example. Even though the value in auth module changes, it doesn't update in App.js So how can I update the App.js's state from Sign in screen, for example?
import React, { Component } from 'react';
import { AppRegistry, Platform, StyleSheet, Text, View } from 'react-native';
import DrawerNavigator from './components/DrawerNavigator'
import SignedOutNavigator from './components/SignedOutNavigator'
import auth from './auth'
type Props = {};
export default class App extends Component<Props> {
constructor(props) {
super(props)
this.state = {
isLoggedIn: auth.isLoggedIn
}
}
render() {
return (
(this.state.isLoggedIn) ? <DrawerNavigator /> : <SignedOutNavigator />
);
}
}
AppRegistry.registerComponent('App', () => App)
and my auth module, which is very simple
import { AsyncStorage } from 'react-native';
// try to read from a local file
let api_key
let isLoggedIn = false
function save_user_settings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
isLoggedIn = true
});
}
module.exports.save_user_settings = save_user_settings
module.exports.api_key = api_key
module.exports.isLoggedIn = isLoggedIn
First off, there are loads of ways to approach this problem. Because of this I'm going to try explain to you why what you have now isn't working.
The reason this is happening is because when you assign auth.isLoggedIn to your isLoggedIn state, you are assigning the value once, kind of as a copy. It's not a reference that is stored.
In addition to this, remember, React state is generally only updated with setState(), and that is never being called here, so your state will not update.
The way I would approach this problem without bringing in elements like Redux, which is overkill for this problem by itself, is to look into building an authentication higher order component which handles all the authentication logic and wraps your entire application. From there you can control if you should render the children, or do a redirect.
Auth Component
componentDidMount() {
this._saveUserSettings(settings);
}
_saveUserSettings(settings) {
AsyncStorage.mergeItem('user', JSON.stringify(settings), () => {
AsyncStorage.getItem('user', (err, result) => {
isLoggedIn = result.isLoggedIn
api_key = result.api_key
});
this.setState({isLoggedIn: true});
});
}
render() {
const { isLoggedIn } = this.state;
return isLoggedIn ? this.props.children : null;
}
App.js
render() {
<AuthComponent>
//the rest of authenticated app goes here
</AuthComponent>
}
Here's a really quick, incomplete example. But it should showcase to you how you may want to lay your authentication out. You'll also want to consider error handling and such, however.

It is possible force screen update after navigation.goBack() in React Native?

Exists some way to do this?
When I use { navigation.goBack() } my changes wasn't does updated in the before screen, but using navigate('MyObject', params ) the changes will be made without additional code to receive the changes.
There are 2 ways I can think of for you to do this. The first is to use Redux and the second is to pass a function to the next screen that will update the previous screen. Example (for the second method) are below:
export default class screenA extends Component {
constructor(props) {
super(props);
this.state = {value: 0};
}
updateValue=(val)=>{
this.setState({value: val});
}
openScreenB=()=>{
this.props.navigation.navigate('screenB', {
updateValue: this.updateValue,
});
}
}
export default class screenB extends Component {
updateClassAValue() {
const {params} = this.props.navigation.state;
params.updateValue(20);
this.props.navigation.pop();
}
}
For Redux, I can't really say much about it since I haven't used it that much. However, it's made for this kind of purpose of sharing values between screen and easily updating the values.

How to get state updated after dispatch

I'm new with react-native and redux and I want to know how can I get the state updated after the dispatch...
Follow my code:
/LoginForm.js
function mapStateToProps(state) { return { user: state.userReducer }; }
function mapDispatchToProps(dispatch) {
return {
login: (username, password) => {
dispatch(login(username, password)); // update state loggedIn
}
}
}
const LoginForm = connect(mapStateToProps, mapDispatchToProps)(Login);
export default LoginForm;
/Login.js
---Here I've a button which calls this method loginOnPress()
loginOnPress() {
const { username, password } = this.state;
this.props.login(username, password);
console.log(this.props.user.loggedIn)
}
According my code above, I call first the method 'this.props.login(username, password);' that calls the dispatch and change the state 'loggedIn'.
And after that I try to get the state updated but without success:
console.log(this.props.user.loggedIn)
Note: When I click the second time on this button the state comes updated
Calling dispatch will update the state immediately but your components will be updated a little bit later so you can use componentWillReceiveProps to react to changes in the props, you can have a look here for a better explanation of how state change works in React
The function this.props.login(username, password) dispatches a login action on the redux-state.
Launching store.getState() will indeed immediately get you the redux-state after the update, but usually, you don't really need to do it because of the redux connect function that wraps your component.
The redux connect function updates your component with new props, so what you would usually do is "catch" these changes in one of the following functions of the react lifecycle:
class Greeting extends React.Component {
...
loginOnPress () {
const { username, password } = this.state;
this.props.login(username, password);
}
// before the new props are applied
componentWillReceiveProps (nextProps) {
console.log(nextProps.user.loggedIn)
}
// just before the update
componentWillUpdate (nextProps, nextState) {
console.log(nextProps.user.loggedIn)
}
// immediately after the update
componentDidUpdate (prevProps, prevState) {
console.log(this.props.user.loggedIn)
}
render() {
...
}
}

Realm & React Native - Best practice to implement auto-updates?

What are the best practices/patterns make realm a reactive datasource in a react native app? Especially for presentational and container components pattern?
Here is an example which I'd like to make reactive: Realm with React Native
The docs on auto-updates/change-events are a bit thin and the official example does not make use of this feature (to my knowledge).
You can make your example reactive by subscribing to events and updating the ui when you receive a change event. Right now events are only sent when write transactions are committed, but finer grained change events will be added in the future. For now you could add the following constructor to update the ui on changes:
constructor(props) {
super(props);
this.realm = new Realm({schema:[dogSchema]})
this.realm.addListener('change', () => {
this.forceUpdate()
});
}
You need to hold onto a Realm instance to keep the notifications alive, and you can use this Realm instance throughout the rest of the component.
Instead of calling forceUpdate, you could instead set the component's state or props within the event listener to trigger the refresh, like so:
constructor(props) {
super(props);
this.realm = new Realm({schema:[dogSchema]})
this.state = {...}; // Initial state of component.
this.realm.addListener('change', () => {
this.setState({...}); // Update state instead of using this.forceUpdate()
});
}
I think #Ari gave me a good answer for redux folks as i was also struggling. I'm not sure if it's immutable enough but it works!
I'm simpliy dispatching getVehicles action inside addListener and it just works!
Below is UI component whose constructor function makes the magic!
//- importing my realm schema
import realm from '../../db/models';
//- Importing my action
import { getVehicles } from './../../actions/vehicle';
#connect((store) => {
return {
vehicle: store.vehicle.vehicles
}
})
export default class Devices extends Component {
constructor(props) {
super(props);
realm.addListener('change', () => {
props.dispatch(getVehicles());
});
}
}
Below is db/models file used up there in the constructor.
import Realm from 'realm';
class VehicleSchema {};
VehicleSchema = {
name: 'vehicleInfo',
properties: {
vehicleName: 'string',
vehicleNumber: 'string',
vehiclePassword: 'string',
vehiclePasswordTrigger: 'bool',
vehicleType: 'string',
vehiclePicture: { type: 'data', optional: true }
}
};
export default new Realm({schema: [VehicleSchema]});
Below is the actions/vehicle file, which gets dispatched in the constructor above.
import { queryVehicle } from './../db/queryVehicle';
export function getVehicles() {
const vehicles = queryVehicle();
return function(dispatch) {
dispatch({type: "GOT_VEHICLES", payload: vehicles});
}
}
Below is my queryVehicle function that does the querying called in action file above.
import vehicleModel from './models';
const queryVehicle = (queryInfo="vehicleInfo", filter='') => {
const objects = vehicleModel.objects(queryInfo);
if(filter.length === 0) return objects;
let results = objects.filtered(filter);
return results;
};
export { queryVehicle };
disclaimer I don't know if this code looks immutable enough, or following good redux practice cause i'm just starting out with redux so give me some comments advising if i'm doing something wrong.
I'll also guess reducer implementation wouldn't matter much in this here.
Recently ran into an issue with Realm ListView auto-updating. When the ListView rows have varied heights, you can get overlaps on rows in the UI. The below was the only way I could get the ListView to re-render without causing UI overlaps. It seems a bit "dirty" to me, so if there is a better way, I welcome the input. But this is working perfectly so far; incase anyone else runs into this issue.
Basically it just wipes the dataSource, then inserts it again using the setState callback when there are insertions or deletions, but modifications simply roll through and auto-update.
let feed = this.props.store.feed;
feed.addListener((name, changes) => {
if (changes.insertions.length || changes.deletions.length) {
this.setState({dataSource: this.ds.cloneWithRows([])},
() => this.setState({dataSource: this.ds.cloneWithRows(feed)})
);
} else {
this.setState({dataSource: this.ds.cloneWithRows(feed)});
}
});