How do I refresh my data when navigating back? For example, if I want to edit something and then press the "Back arrow", I want to immediately see the changes.
componentWillMount() {
getData("data", (res) => {
this.setState({ name: res.name, date: res.date});
var date = new moment(this.state.date);
var currentDate = new moment();
var duration = moment.duration(date.diff(currentDate)).as("days");
duration = Math.round(duration + 1).toString();
var daysLeftPercent = Math.round([duration / 340] * 100);
this.setState({ daysLeft: duration, daysLeftPercent: daysLeftPercent});
});
}
Like this but I also want to run it when pressing the "back arrow" on another page that leads to this.
If you want to access a component's method from another component, you should bind it(may within props or navigation params).
And to access 'back arrow'(i think you means devıce's back button otherwise its up to you) you can use BackHandler from react native.
BackHandler.addEventListener(
"hardwareBackPress",
this.props.onPressBackButton
);
...
onPressBackButton = () => {
//do something
}
You if you want to navigate back to a previous component and pass data to the previous component you can use () => this.props.navigation.goBack()
For example if you wanted to click a button to go back:
<TouchableHighlight onPress={() => this.props.navigation.goBack(
//do something here
)}>
<Text> Go Back to previous component </Text>
</TouchableHighlight>
How to Make the Screen Refresh when Navigating Back in React Native----
React.useEffect(() => { const unsubscribe = navigation.addListener('focus', () => { Alert.alert('Refreshed'); }); return unsubscribe; }, [navigation]);
use this into useefect and before render/return
Related
I want to update the previous screen's useState value and refresh the screen on click of goBack(), I know there is a useIsFocused() to do that but using this screen it is refreshing every time i goBack to the screen, suppose there is a ScreenA and ScreenB, So when user is performing any action in ScreeB then I am dispatching a value using redux and on goBack I am updating the useState value and refreshing the screenA, But it not working, I don't know what's the problem, Please help.
ScreenB
const leaveGroup = () => {
hideMenu();
callLikeMindsApi(`URL`, httpMethods.HTTP_PUT)
.then(response => {
if (response && response.data && response.data.success) {
hideMenu();
dispatch(updateHomeScreen(true));
props.navigation.goBack();
}
})
.catch(error => {
Alert.alert('FAILED', error.response.data.error_message);
});
};
And In ScreenA
const {updateValue} = useSelector(state => state.homeReducer);
useEffect(() => {
console.log('updateValue-1234', updateValue);
}); // it is printing true in console if (updateValue) { setOffset(1); setTotalPages(1); setMyChatRooms([]); getUserData(); } }, [isFocused]);
But
setOffset(1);
setTotalPages(1);
setMyChatRooms([]);
values are not updating, if I remove the if(updateValue){} and writing like this
useEffect(() => {
setOffset(1);
setTotalPages(1);
setMyChatRooms([]);
getUserData();
}, [isFocused]);
then code is working as expected, But it is refreshing every time I come back to the screenA and I want to refresh conditionally.
instead of props.navigation.goBack(); do
props.navigation.navigate('ScreenA',{details});
with the prop that you want pass
in screen A get that value in state from route params
ScreenA = (props) => {
const [navigateState] = useState(props.route.params.details|}{});
---while navigating to Screen B---
props.navigation.navigate('ScreenB',{details: navigateState});
...
I can't get full your code and understand the full logic but if you have already implemented redux you should use it like this:
const {updateValue} = useSelector(state => state.homeReducer);
useEffect(()=>{
if (updateValue) {
// update your screen juse by setting some states on the current component.
} else {
// don't update
}
}, [updateValue])
if you dispatch true value for updateValue whereever in your app then screen-A would be updated regardless it's getting focus or not. if you want to update screen-A only when it's on focus you should add focus condition in useEffect.
ComponentDidmount loads once in a lifetime. If I open a tab then it will be called but if I switch to another tab and then go to the first tab componentdidmount will not call again. I need some method which will call even when tab has been rendered earlier and I visit other tab then come back to first tab and it again runs.
Use this focus listener.
componentDidMount() {
this._unsubscribe = this.props.navigation.addListener('focus', () => {
// do something
});
}
you should add Focus listener in your project like that :
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener('didFocus', () => {
console.log("refresh shod");
});
}
and this for remove listener :
componentWillUnmount() {
this.focusListener.remove();
}
I'm trying to test the OnDidFocus event in my React Native app using react navigation 4 and using the following event listener:
useEffect(() => {
const willFocusSub = props.navigation.addListener(
"onDidFocus",
console.log("testing onDidFocus")
);
return () => {
willFocusSub.remove();
};
});
When I first load the page it works fine but when I move away and then come back to the same screen through the Back button it does not seem to perceive the focus event.
This is my stack
const MovieNavigator = createStackNavigator(
{
MoviesList: HomeMovies,
MovieDetail: MovieDetailScreen,
PopularMovies: PopularMoviesScreen,
CrewMember: CastDetailScreen,
GenreSearch: GenreSearchScreen,
MovieSearch: MovieSearchScreen,
},
I'm in MoviesList and the event is triggered fine, then I move to MovieDetail. If I hit Back and return to MoviesList the event onDidFocus is not triggered at all.
I think you could try "willFocus" instead.
Like this:
const willFocusSub = props.navigation.addListener(
"willFocus",
()=>{console.log("testing willFocus")}
);
Try modyfying your useEffect call to this!
useEffect(() => {
const willFocusSub = props.navigation.addListener(
"onDidFocus",
console.log("testing onDidFocus")
);
return () => {
willFocusSub.remove();
};
},[]);
I found another way to detect the focus and blur event and seems the only way to track an event when using the Back button.
Instead of subscribing to events, I'm check the focus status of the screen using the useIsFocused() hooks available from react-navigation-hooks library.
import { useIsFocused } from "react-navigation-hooks";
...
const [showGallery, setShowGallery] = useState(true);
...
useEffect(() => {
if (isFocused) {
setShowGallery(true);
} else {
setShowGallery(false);
}
console.log("isFocused: " + isFocused);
}, [isFocused]);
Basically I'm checking the status of the screen using isFocused hook every time it changes (when it leaves and returns only same as didFocus and didBlur) and setting the state setShowGallery accordingly to run the carousel when focused and stop it when blurred.
Hope it helps others!
I am building a kind of book app (Holy Quran )... user will go for a list of Surahs, each Surah contains around 5 - 50 pages. I managed navigating user from the list to first page of each Surahs.. and through getting an api request data for first page will be shown and this is the code in the showScreen
const [quran, setQuran] = useState([]);
const page = navigation.getParam('page');
const name = navigation.getParam('name');
let pageNumber = page;
useEffect(() => {
Quran();
}, []);
const Quran = async () => {
const response = await QuranApi.get(`/${pageNumber}/quran-uthmani`);
setQuran(response.data.data.ayahs);
}
so let's imagine that first page is page number 200, I am looking for some way so when user clicks go to page 201 or 199 (next or previous) and refetching the data so show for him requested page
I need some help here please and thanks in advance
Basically you need to add some sort of button or any element in your 'markup section' which will trigger next/previous action. For example:
// The following line makes react listen for changes in page state variable.
// If you use setPage() anywhere, react will auto update all the components
// where page variable is used. You don't need to manually do it
const [page, setPage] = useState(nav.getParam('page'))
// ....
// This function does exactly that
const handleClick = event => {
if(event.target.value === 'next-page') // If next button was pressed
setPage(page + 1) // We increment the page state variable by 1
else if(event.target.value === 'prev-page') // If prev button was pressed
setPage(page - 1) // We decrement the page state variable by 1
// By doing so, react will auto update every element which uses this variable
}
// .....
// We tell react, if [page] state variable changes by user clicking
// next or previous button, fetch data from the api using updated
// page number and using that, we update the [quran] variable using
// 'setQuran()' function
useEffect(async () => {
const response = await QuranApi.get(`/${page}/quran-uthmani`)
setQuran(response.data.data.ayahs)
}, [page] );
//......
// markup section
return(
//....
<Button onClick={handleClick} value="next-page">Next Page {page + 1}</Button>
<Button onClick={handleClick} value="prev-page">Prev Page {page - 1}</Button>
//....
)
Thank you dear #Sayed it finally works but I have to make some modifications with the same idea to be
const [page, setPage] = useState(navigation.getParam('page'))
const handleRightClick = () => {
setPage(parseInt(page) + 1)
};
const handleLeftClick = () => {
setPage(parseInt(page) - 1);
}
useEffect(() => {
return () => {
console.log("cleaned up");
};
}, []);
useEffect(() => {
Quran();
}, [page]);
<Button title = 'Next Page' onPress = {handleRightClick}/>
<Button title = 'Prev-page' onPress ={handleLeftClick} />
So it's working well in that case without an error
Try this small change...
const [page,setPage]=useState(nav.getParam('page'))
useEffect(() => {
Quran();
}, [page]);
by passing an empty array as a second parameter to the useEffect function, you are telling react to only execute the side effect once (at mount time).
However if that array contains a list of state variables to watch for. React will only re-run the side effect if one of the items in this array changes.
The above mentioned change in useEffect would make your useEffect sensitive to any change in the state variable (page) passed to the useEffect array. As you pass new pagenumbers in the url as a parameter, it would result in calling useEffect everytime as the page state variable is set.
Hope this helps!
I'm using a List View
class GroupList extends Component {
componentWillMount() {
this.props.fetchGroups();
I fetch from firebase a VERY SHORT list of elements.
export const fetchGroups = () => {
const { currentUser } = firebase.auth();
return (dispatch) => {
dispatch({
type: FETCHING_GROUPS
});
firebase.database().ref(`/users/${currentUser.uid}/groups`)
.on('value', snapshot => {
dispatch({
type: GROUPS_FETCHED,
payload: snapshot.val()
});
});
};
};
To create a new item I use the top-right Button offered from react-native-router-flux
<Scene
key="groupList"
component={GroupList}
title="Gruppi di servizio"
hideNavBar={false}
rightTitle="Nuovo"
onRight={() => Actions.groupForm({ formType: NEW_GROUP_FORM_TYPE })}
sceneStyle={{ paddingTop: 65 }}
/>
So I click top-right 'new' button and I see my form.
User then save the new group name
This is my action to Save to firebase:
return (dispatch) => {
dispatch({
type: TRYING_TO_CREATE_GROUP,
});
firebase.database().ref(`/users/${currentUser.uid}/groups`)
.push({ groupName })
.then(() => {
dispatch({ type: CREATE_GROUP_SUCCESS });
Actions.groupList({ type: 'reset' });
})
.catch((errorText) => {
dispatch({
type: CREATE_GROUP_FAILED,
payload: errorText
});
});
};
What's the problem?
After the first round of
Actions.groupForm({ formType: NEW_GROUP_FORM_TYPE })}
and
Actions.groupList({ type: 'reset' })
My GroupList is succesfully refreshed, because componentWillMount is called again.
The problem is that after the SECOND round of groupForm -> groupList my list is no more refreshed.
Adding a console log to componentWillMount I noticed that it's no more executed.
Why?
What's the right way to return to the Scene 'groupList' , forcing the refresh at any cost?
I solved this by doing the following
Actions.replace(sceneKey, props );
For your code
Actions.replace('groupForm', { formType: NEW_GROUP_FORM_TYPE } );
Hope this help!
This has worked for me. Try it.
The gist of it is when you define a Scene, you can override its "onBack" function. In order to force refresh of the previous page, you have to provide it with a prop with a different value each time, thus the Math.random().
<Scene
key='title'
component={YourComponent}
title='Title'
onBack={() => Actions.pop({ refresh: Math.random() })}
/>
I haven't worked with react-native-router-flux but I've run into the same problem and hopefully can give you enough to figure out how to do it with that library.
Basically It doesn't get called again (componentWillMount) because the component is already mounted and its just returning focus to that component. What you have to do is pass that fetchGroups() to wherever the next scene is and call it again once you're returning back to the GroupList scene which will cause a re-render with the most updated information. Let me know if I should clarify further.
Inspired by #Matt's solution, I solved my problem. And I am gonna post my sample code.
The idea is that if you wanna refresh the parent page while routing back from a child page, the way I do it is to pass a destroy and an update callback function to the child. The destroy function makes sure that the page falls back to its initial state, while the update function will fetch the latest data.
// parent scene
Actions.childSceneName({
onBack: () => this.props.fetchData(),
destroyParent: () => this.props.destroyPage(),
})
So that the child page can invoke the functions before routing back.
// child scene
componentWillUnmount() {
this.props.destroyParent()
this.props.onBack()
}
The best way is using a State Manager OR the new Context API of React.
This is the flow:
Entering into ScreenOne
From ScreenOne open ScreenTwo (both using state management)
When finishing all the actions in ScreenTwo, call your function that reloads ScreenOne, at the same time use Actions.pop() to leave current screen (leaving ScreenTwo, and getting back into ScreenOne).
Done! your ScreenOne is reloaded with new data.
Summary: The key of this is calling your function that reloads
Previous Screen.