It is possible force screen update after navigation.goBack() in React Native? - 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.

Related

How to refresh a screen when returning from another screen of a different navigator (React Native)?

I have been implementing most of my application with a StackNavigator. Now, I added a DrawerNavigator, from which one of its screens calls another screen of the original StackNavigator. For example, consider the following navigation sequence that a user could make:
ScreenA -> ScreenB -> ScreenC
where ScreenA belongs to the StackNavigator, ScreenB belongs to the DrawerNavigator, and ScreenC belongs to the StackNavigator again. To achieve that, actually ScreenA does not call ScreenB directly, but another screen whose sole purpose is to serve as a root of all the screens that belong to the DrawerNavigator. Also, that root receives the StackNavigator in the ScreenProps in order that its screens can later use the Stack again.
Now, if I am in ScreenC and I go back using "this.props.navigation.goBack()", I return to the DrawerNavigator in the ScreenB, because that is which called ScreenC. The ScreenB should refresh its state, that is, it should reload information from the database, because that information could have changed in ScreenC, so the previous state is no longer valid.
When only using StackNavigator, I always managed to do it using "NavigationEvents". For example:
import {Component} from 'react'
...
import { NavigationEvents } from 'react-navigation'
class ScreenB extends Component{
// This is the function that loads information from the database (PouchDB)
loadInformation = async() =>{
...
}
render(){
return(
<View>
<NavigationEvents onWillFocus = {payload => this.loadInformation()}/>
<NavigationEvents onDidFocus = {payload => this.loadInformation()}/>
...
</View>
)
}
}
With this implementation, the function "loadInformation" activated both when I entered the screen for first time, and also when I returned to it from a child screen. But this time that I am mixing both navigators, neither "onWillFocus" nor "onDidFocus" are activating when returning from ScreenC to ScreenB, so I cannot enter to the "loadInformation" function again. How could I do it?
Edit:
I also tried keeping a boolean variable in Redux store that determines if the function "loadInformation" of ScreenB must be activated. That variable starts with the true value. Then, once I enter to Screen B and I execute the function, it is changed to false. When I navigate to ScreenC, in that screen the variable is changed to true again, so when I go back to ScreenB it indicates again that the function must be executed.
That required to use in ScreenB the "componentDidUpdate" function, that constantly checks if that variable is true or false in order to call "loadInformation". That solved the problem, but brought a new one. When I try to navigate from ScreenB to another screen of the DrawerNavigator, it takes too much time, because in the transition "componentDidUpdate" is called repeatedly. So this solution does not seem viable.
Unfortunately the approach you used <NavigationEvents> has been updated. so, what should you do is:
class screenA/ screenB/ screenC extends React.Component {
componentDidMount() {
this._unsubscribe = navigation.addListener('focus', () => {
// do something
});
}
componentWillUnmount() {
this._unsubscribe();
}
render() {
// Content of the component
}
}
Use these updated navigation events in all of your screens. Hope it will solve your issue. For more information See This
I am answering my own question.
The solution was to use a boolean variable from Redux's store that indicates if the function "loadInformation" must be activated or not. Let's say the variable is named "loadView", which has the value "false" by default, but the ScreenC sets it in "true" when it is going to be closed and therefore we are going to return to ScreenB.
In other words, the file of ScreenC includes this code:
import {Component} from 'react'
import { connect } from 'react-redux'
// Here we import the action that allows to change the value of "loadView"
import { changeLoadView } from '../../redux/actions/popUpActions'
...
class ScreenC extends Component{
...
// Function that is activated automatically when we leave the screen
componentWillUnmount(){
// This is the function that assigns the value "true" to "loadView"
this.props.dispatchChangeLoadView(true)
}
...
}
const mapStateToProps = (state) => {
return {
...
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchChangeLoadView: (bool) => dispatch(changeLoadView(bool)),
....
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenC);
In the file of ScreenB I use a "fake view", which is a React View that is not placed directly in the "render" function but it is called by another function (named "activateLoadInformation" in this case). That function returns an empty view, but the reason to use it is because before its "return" we can activate any other function of ScreenB that we want, that in this case is "loadInformation". I don't know another way to activate functions that don't render anything arbitrarily when we want to.
import {Component} from 'react'
import { connect } from 'react-redux'
...
class ScreenB extends Component{
...
// This is the function that loads information from the database (PouchDB)
loadInformation = async() =>{
this.props.dispatchChangeLoadView(false);
...
}
// Fake view that calls the function "loadInformation"
activateLoadInformation(){
this.loadInformation();
return(<View/>)
}
render(){
return(
<View>
{!this.props.loadView &&
<NavigationEvents onWillFocus = {payload => this.loadInformation()}/>
}
{this.props.loadView &&
this.activateLoadInformation()
}
...
</View>
)
}
}
const mapStateToProps = (state) => {
return {
loadView: state.popUpReducer.loadView,
}
}
const mapDispatchToProps = (dispatch) => {
return {
dispatchChangeLoadView: (bool) => dispatch(changeLoadView(bool)),
....
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ScreenB);

The action POP with payload was not handled by any navigator

I have no idea what is causing this bug in my react native app. I'm using version 5 of the React Navigation library.
It randomly crashes the app sometimes. Google searching hasn't helped me understand what this is. It's very selective though which is a good(or a bad) thing.
So what does this mean and what could be causing it?
if(navigation.canGoBack()) {
navigation.dispatch(StackActions.pop(1));
}
see https://github.com/react-navigation/react-navigation/issues/7814#issuecomment-599921016
Did you try this?
this.props.navigation.goBack()
It means you tried to pop a view where there was nothing to pop. It might mean there's a bug in your app because, generally, you shouldn't be popping a view when there isn't any to pop.
But it can also be part of intentional design where you have insufficient knowledge of the current navigation state, but need to make sure at least one pop is done (similar to clearing a flag variable even if it might not be set in the first place, in which case it would be a no-op). If that's the case, then you can disable this development-level warning:
const temp = console.error;
console.error = () => {};
navigation.pop();
console.error = temp;
Error Cause: goBack() or pop() is getting called multiple times. Sometimes onPress event gets called many times. You can check by adding console.log().
How to Solve: you need to throttle the onPress function.
Example:
import React, { PureComponent } from 'react'
import { Text, View } from 'react-native'
import { throttle } from 'lodash'
export default class Test extends PureComponent {
constructor(props) {
super(props)
this.state = {
}
this.onPress = throttle(this.onPress, 500, {trailing: false})
}
onPress = () => {
console.log("going back")
this.props.navigation.pop();
//this.props.navigation.goBack();
}
render() {
return (
<View>
<Text>Hello World!</Text>
</View>
)
}
}
you need to check there can go back or not by canGoBack method like this
import { StackActions } from '#react-navigation/native';
if(this.refs.navigation.canGoBack())
{
this.refs.navigation.dispatch(StackActions.pop(1));
// this.refs.navigation.dispatch(StackActions.popToTop());
}

React Native with Redux: mapStateToProps()-function is not updating

I am working at a React Native and Redux project with several reducers, that are combined through combineReducers().
I have one component, that doesn't dispatches any actions but only shows information from the redux state. It is connected to this state with the connect function:
class MyComponent extends React.Component {
componentWillMount() {
// some code
}
render() {
return(
<Text>
{ this.props.value }
</Text>
)
}
}
function mapStateToProps(state) {
return {
value: state.updaterReducer.value,
}
}
export default connect(
mapStateToProps,
) (MyComponent);
There's another part/component in the project, that changes the redux state and value:
import { makeUpdate } from './updater.actions.js';
import configureStore from '#redux/configureStore.js';
const store = configureStore();
// This function is can be called by some buttons in the app:
export default function update() {
console.log('Update function called');
store.dispatch(makeUpdate());
}
And here's my problem: when update is called, the updater-action and the updater-reducer are both called, so that the redux state changes, but 'MyComponent' never updates.
I could solve the problem on my own: The solution was very easy in the end, but couldn't be found on the basis of the code in the original question: Every time I needed the redux-store, I used a configureStore-function from a web-tutorial to create it on basis of the reducers. So I created multiple times the 'same' store. Unfortunately these stores were not connected to each other...
Sometimes that worked in the project, because a mapStateToProps-function and a mapDispatchToProps-function both were in the the same component and used one store, but sometimes (like in the example in the question) those functions used different stores and couldn't influence each other.

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

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.

Navigation - Pass variable to other files

I'm new on React-Native and it's my first React-Native app. However, I have already some problems.
I want to pass a variable from one class (Home.js) to an another. (Is it possible without using the composent in the render() fonction ?)
##### Home.js #####
class Home extends Component {
constructor(props) {
super(props);
this.state = {direction: "defaultvalue"};
}
getCurrentDirection() {
return this.state.direction;
}
render() {
/***..... some elements ..*/
}
}
export default Home
And
#### Two.js ####
import Home from './Home'
/** SOME CODE **/
const DrawerOptions = {
initialRouteName: Home.getCurrentDirection(),
contentComponent: CustomDrawerContentComponent,
drawerWidth: 300,
};
However it doesn't work... How to resolve it ? I have already try some solutions as declare the getCurrentDirection as static but nothing.
In addition, it seems to be a specific case because DrawerOptions is not a class. Could you please, add to your response also, how make it if I want to obtain the variable into the class Two.js ?
I meant if Two.js was for example :
##### Two.js #####
class Two extends Component {
var myvariable = Home.getCurrentDirection();
render() {
/***..... some elements ..*/
}
}
Thanks a lot in advance
A recommendable way of accessing the state from a component into another is to use (in this case) the Home component as a parent of Two component. This way you don't have to trigger a function to access the Home's state. On each time when the state of the parent (in this case) component will be updated, the Two component will receive the updated property (direction). If you want to call a function from Two component, you have to pass it a function as a property (changeCurrentDirection) that will call back the function you want to trigger from Home component.
So you would have something like this:
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
direction: "defaultValue"
};
}
changeCurrentDirection() {
this.setState({
direction: "valueChanged"
})
}
render() {
let state = this.state;
return (
<Two
direction={state.direction}
changeCurrentDirection={() => this.changeCurrentDirection.bind(this)}/>
)
}
}
class Two extends React.Component {
render() {
let props = this.props;
return (
<div>
<h3>{props.direction}</h3>
<button onClick={props.changeCurrentDirection()}>Change value</button>
</div>
)
}
}
React.render(<Home/> , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script>
<div id="app"></div>
Additional info you can find here.
Also, if you want to have a good management of the state of your components, my advice for you is to use redux. Using this library you can easily connect the component's actions and properties that can further be accessible from other files where you can manage them.