how to change params value passed to axios route to change data react native? - react-native

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!

Related

How to update the previous Screen's useState value and refresh the screen on click of goBack()

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.

params is cached and shows older values on return to the same screen

I use react-navigation 5 and having issues because the params shows older values.
ScreenList.js
const handleFirstUser = () => {
const userDetail = {'name': 'First User'};
navigation.navigate('Details', { detail: userDetail});
}
const handleSecondUser = () => {
const userDetail = {'name': 'Second User'};
navigation.navigate('Details', { detail: userDetail});
}
The methods are called from a button and do go to Details Screen but the value is whichever was loaded initially:
ScreenDetails.js
const DetailsScreen = (props) =>{
const { navigation, route } = props;
const { params } = route;
const userDetail = params.name;
}
First time the app the loads, first user is loaded and correctly shows name. Then go back to the list screen, click user two. Still the first user is shown. I tried to clean up with useEffect but it doesnt have any effect:
useEffect(()=>{
return(()=>{
navigation.setParams({name: undefined});
});
}, [])
how can I reset setParams each it laods and use the values passed to it currently?

OnDidFocus event not working when you navigate back from the stack

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!

Getting updated redux state after dispatch with react hooks

I'm using the action expensesActions.getExpenseList to get a list from the database, which in turns updates the store in expense.expenseList
I'm calling the action inside useEffect hook and would like to get back the list from the store once it's retreived.
My code below is not working because the order is incorrect, if I refresh (with save) I do have the list. How can I change the code so my list is retreived once the actions is complete?
const fetchedList = useSelector(state => state.expense.expenseList);
// Get expense list
useEffect(() => {
const loadList = async () => {
setIsLoading(true)
await dispatch(expensesActions.getExpenseList())
calculateAverageExpense()
}
loadList()
}, [dispatch]);
You can have a second useEffect like this:
useEffect(() => {
if (fetchedList.length) calculateAverageExpense();
}, [fetchedList])
This will execute calculateAverageExpense every time your list change.

How to remount a screen from another screen? (Refresh the whole app again with new parameters)

I have a configurable application which everything is fed into the app from a middleware (like colors and contents) based on a unique id so-called appId.
In the home screen, I am fetching all required data from a middleware in componentDidMount() function and then use it later on. For the first time, I am using a default appId and the componentDidMount() looks like this:
componentDidMount() {
this.setState({ isLoading: true });
fetch(
API +
"configurations" +
"?" +
"uuid=blabla" +
"&" +
"appId=" +
appId +
"&" +
"locale=" +
locale +
"&" +
"gid=" +
gid,
{
method: "GET",
headers: {
Accept: "application/json"
}
}
)}
I have another screen (settings screen) where I have a box and the user can insert appId as input.
When the appId is inserted by the user (in the settings page), I would like to navigate back to the Home screen and re-fetch the data with the new appId that was inserted by the user. The setting screen looks like this:
state = {
newappId: "" };
handlenewappId = text => {
this.setState({ newappId: text });
};
.....
<Item regular>
<Input
onChangeText={this.handlenewappId}
placeholder="Regular Textbox"
/>
<Button
onPress={() => {
navigation.navigate("Home");
}}
>
<Text>Save</Text>
</Button>
</Item>
However, when I do navigation.navigate("Home") the componentDidMount() is not triggered in order to fetch the data again from the middleware (which is expected since it is only triggered for the first time).
What should I do? What is the solution?
I have already tried the solution given in `componentDidMount()` function is not called after navigation
but it didn't work for me.
also tried to move the code in componentDidMount() into a separate function and call it from the settings page but I couldn't make it work.
============== UPDATE: ==============
I was able to solve the issue with the answer given by "vitosorriso" below. However, a new issue occurs. After fetching is done, I am pushing the response to the state and then use it my home screen like this:
fetchData = async () => {
this.setState({ isLoading: true }, async () => {
//fetch the data and push the response to state. e.g:
this.setState({ page: data, configs: data2, isLoading: false });
}}
....
render() {
const { configs, page, isLoading, error } = this.state; //getting the data fetched in the fetch function and pushed to the state
if (isLoading || !page || !configs) {
//if data is not ready yet
);
// Use the data to extract some information
let itemMap = page.item.reduce((acc, item) => {
acc[item.id] = item;
item.attributes = item.attributes.reduce((acc, item) => {
acc[item.key] = item.value;
return acc;
}, {});
return acc;
}, {});
}}
For the first time the app starts, everything works fine and there is no error but if I go to the settings page and press the button to navigate back to the home screen and fetch data again, I face the error:
"items.attributes.reduce is not a function".
I am assuming the reason is, "items.attributes" already has a value (from the first time) and can't fed with new data again.
Is there any way, to clear all the variables when navigating from settings page to the home page?
I have solved the same problem in my app with a similar concept of this ( `componentDidMount()` function is not called after navigation ) but using a different syntax, and it is working for me:
// your home class
// no need to import anything more
// define a separate function to fetch data
fetchData = async () => {
this.setState({ isLoading: true }, async () => {
// fetch your data here, do not forget to set isLoading to false
}
}
// add a focus listener onDidMount
async componentDidMount () {
this.focusListener = this.props.navigation.addListener('didFocus', async () => {
try {
await this.fetchData() // function defined above
} catch (error) {
// handle errors here
}
})
}
// and don't forget to remove the listener
componentWillUnmount () {
this.focusListener.remove()
}