React Native refresh content when user hits back button - using Hooks - reactjs

I would like to refresh the data, when user is back from one page to another.
This is how my useEffect function looks like now:
useEffect(() => {
setIsLoading(true);
AsyncStorage.getItem("user").then((response) => {
const currentData = JSON.parse(response);
setUser(currentData)
fetch('URL',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: currentData.id
}),
}
)
.then(response => response.json())
.then(data => {
setNotis(data.notifications);
setIsLoading(false)
})
.catch(error => {
});
});
}, []);
This function should run every time when user is on the page. Doesn't matter if it was onBackPressed or not.
Thanks

Using React-navigation
We can directly refresh screen if you're using react-navigation
Import #react-navigation/native
import React, { useEffect } from "react";
import { useIsFocused } from "#react-navigation/native";
const HomeScreen = (props) => {
const isVisible = useIsFocused();
useEffect(() => {
console.log("called when screen open and also on close");
// this will call on both screen open and screen close.
if (isVisible) {
console.log("called when screen open or when back on screen ");
}
}, [isVisible]);
return (
......
)
}
I hope it will help.

The real problem here is that the screen is not being unmounted when navigating outside of it, so the hook won't fire since the component is already mounted. There are multiple options to solve this issue just as adding a listener when the screen gets focused/blurred or just watch for the changes for the navigation prop. For the last workaround, you could try something like:
useEffect(() => {
setIsLoading(true);
AsyncStorage.getItem("user").then((response) => {
const currentData = JSON.parse(response);
setUser(currentData)
fetch('URL',
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
user_id: currentData.id
}),
}
)
.then(response => response.json())
.then(data => {
setNotis(data.notifications);
setIsLoading(false)
})
.catch(error => {
});
});
}, [navigation]);
For watching onFocus event, you could import NavigationEvents from react-navigation and move the logic for the hook inside a function refreshData
import {NavigationEvents} from 'react-navigation`
...
<NavigationEvents onWillFocus={refreshData}/>
Also, you should be setting the isLoading state to false whenever the Promise has settle, for instance you could use
.finally(() => {
setIsLoading(false)
})

Related

Nextjs _app.js shared layout how to run server side ( Closed )

I have a simple nextjs application
=> pages/_app.js
function MyApp({ Component, pageProps }) {
return <Provider store={store}><SiteLayout><Component {...pageProps} /></SiteLayout></Provider>
}
=> layouts/SiteLayout.js : In here, I make a request to api and draw the menu.
export default function SiteLayout({ children }) {
const dispath = useDispatch()
useEffect(() => {
fetch("testlink", {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
method: 'POST',
body: "client=web"
})
.then(response => response.json())
.then(data => dispath(addFeed(data)))
}, [])
return (
<>
<Menu />
{children}
</>
)
}
I know it is not server-side rendered and my problem is that I want to do it server-side because the menu tree can’t show in the source code. I need to request one time and draw a scaffold of the application in SiteLayout shared component. Did anyone solve or face that?
MyApp.getInitialProps = async function (context) {
const data = await fetch("testlik", {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
method: 'POST',
body: "client=web"
}).then(response => response.json())
return {
pageProps:{
menu: data?.data?.menu ?? null
}
}
}
getInitialProps is fixed my issue

React - API call running multiple times

I'm writing a react app that fetches data from two different APIs. In the code below, I call the two APIs then console.log the response. After npm start, in the console, I see that the response has been console.logged a few times. I don't know why it's doing this and it's causing issue with the app's behavior. It's receiving the data from aws dynamoDB tables.
function App() {
const [ response , setResponse ] = useState();
const [ btnText , setbtnText ] = useState('Get Data');
const [ details , setDetails ] = useState();
async function fetchData() {
try {
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setResponse(res))
await fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json())
.then(res => setDetails(res))
} catch (error) {
console.log(error);
};
}
console.log(response)
return (
<div className="container">
<header className='header'>
<button onClick={fetchData}>{btnText}</button>
</header>
<Summary response={response} details={details} />
</div>
);
}
I also tried useEffect to fetch data as soon as app loads, but it's doing the same thing.
useEffect(() => {
try {
Promise.all([
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setResponse(res)),
fetch('url hidden' , {
method: 'POST',
header: {'Content-Type': 'application/json'},
}).then(res => res.json()).then(res => setDetails(res)),
]);
}
catch(err) {
console.log(err);
}
} , [])
this image shows the response after clicking the button only once
When you use console.log in the the functional component you will get that console.log each time the component rerenders. And that happens for example each time you set a new state.
You could just move the console.log inside the fetch function.
Or you just console.log the values in useEffect if they change. Like in the example below.
I also refactored the fetchData function to use async await and Promise.all more efficient.
Also you were missing an "s" for "headers" for the fetch method.
async function fetchData() {
try {
const [response, details] = await Promise.all([
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
(
await fetch("url hidden", {
method: "POST",
headers: { "Content-Type": "application/json" },
})
).json(),
]);
setResponse(response);
setDetails(details);
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
console.log(response, details);
}, [response, details]);
If you move console.log(response) inside fetchData then you will get exact information about how many times handler executes, and it really should be only once, on click.
With your approach you moved log in component body and this will cause log to execute each time element rerenders - probably 3 times: one log initially when element is loaded in DOM, and other 2 when you set states inside handler.

How to add a random number in the following URL in reactjs?

I have a project, where I have a create/delete/update.
so in the update component, I have to update the post Title, post Text, and image. so when I update them and press the submit button to save the changes, it works well but it doesn't display the changed image on the UI, till I reload the page, but it does change the image in the file system,
so the URL is like this: http://localhost:3000/Post-Review/307 i want to add a random number for this URL, after the ID
or if there is any other way to solve this problem.
here is the router: <Route path="/Post-Review/:id" exact> <Post /> </Route>
Here is my submitting code:
const submitUpdate = (e) => {
e.preventDefault();
const formData = postToFormData(
postObject,
file,
selectedTags,
deletedTags
);
formData.append("id", actualId);
axios
.put(`${targetServer}/posts/byId/${actualId}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
accessToken: localStorage.getItem("accessToken"),
},
})
.then((res) => {
if (res.data.error) {
alert(res.data.error);
} else {
history.push("/");
}
});
};
thanks
Couldn't you just use a useEffect inside the component and put the random number in the image ref?
src="myimage.jpg?timestamp=123"
the random number could be the timestamp. This way you would be sure that it will always be a unique value.
const timestamp = new Date().getTime()
#Update
Here is the image URL, that is what I am using now.
src={`${targetServer}/posts/image/${postObject.id}`}
if u want to update the UI, you can simply re-fetch the data after the POST request succeeds. React is built for this kind of use.
here is a sample code :
// your post data goes here
const [data, setData] = useState({
...
})
// a method to fetch post data by id
const fetch_data = (post_id) => {
axios
.get(`${targetServer}/posts/byId/${actualId}`, {
headers: {
"Content-Type": "multipart/form-data",
accessToken: localStorage.getItem("accessToken"),
},
}).then((res) => {
if (res.data.error) {
alert(res.data.error);
} else {
// if the update done, then call fetch
setData(res.data)
}
});
}
// the actual update function
const submitUpdate = (e) => {
e.preventDefault();
const formData = postToFormData(
postObject,
file,
selectedTags,
deletedTags
);
formData.append("id", actualId);
axios
.put(`${targetServer}/posts/byId/${actualId}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
accessToken: localStorage.getItem("accessToken"),
},
})
.then((res) => {
if (res.data.error) {
alert(res.data.error);
} else {
// if the update done, then call fetch
fetch_data(post_id)
}
});
}

Fetching data in React's useEffect() returns data “undefined”

I am trying to fetch data in reactjs with help of use effect hook but after accessing that in return div it is showing data undefined code is given below for that
import React,{ useEffect } from 'react'
export default function Home() {
useEffect(() => {
fetch("https://lighthouse-dot-webdotdevsite.appspot.com//lh/newaudit", {
method: "POST",
body: JSON.stringify({
"url": "https://piyushsthr.netlify.app",
"replace": true,
"save": false
}),
headers: {
"Content-Type": "application/json; charset=utf-8"
},
credentials: "same-origin"
})
.then(res => res.json()) // this is the line you need
.then(function(data) {
console.log(data.lhrSlim[0].score)
//return data;
}).catch(function(error) {
// eslint-disable-next-line no-unused-expressions
error.message
})
}, )
return (
<div>
<h1>{data.lhrSlim[0].score}</h1>
</div>
)
}
can anyone help me to fix this thing
You cannot directly show the content of the request directly on the UI, to do that either it has to be a prop or be stored in the state, for example you could do this.
import React, { useEffect, useState } from 'react'
export default function Home() {
const [State, setState] = useState("");
useEffect(() => {
fetch("https://lighthouse-dot-webdotdevsite.appspot.com//lh/newaudit", {
method: "POST",
body: JSON.stringify({
"url": "https://piyushsthr.netlify.app",
"replace": true,
"save": false
}),
headers: {
"Content-Type": "application/json; charset=utf-8"
},
credentials: "same-origin"
})
.then(res => res.json()) // this is the line you need
.then(function(data) {
setState(data.lhrSlim[0].score);
}).catch(function(error) {
// eslint-disable-next-line no-unused-expressions
error.message
})
}, )
return (
<div>
<h1>{State}</h1>
</div>
)
}
That way you will be able to store the data from the response directly on the state of the component and be able to show it afterwards.
To display the data from the api add a state in your component.
const [score, setScore] = useState([]);
then set score
.then(function (data) {
setScore(data?.lhrSlim[0]?.score);
})
https://codesandbox.io/s/proud-surf-v9gjb?file=/src/App.js

i'm Trying to make react paypal button that change the billing amount on props change

i'm Trying to make react paypal button that changes the billing amount on props change.
I call the following component with props price and everytime the price change i would like to rerender the button to update the actual price.
const PaypalForm = props => {
let paypalRef = useRef();
useEffect(() => {
window.paypal
.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [
{
description: "test",
amount: {
currency_code: "USD",
value: props.price
}
}
]
});
},
onApprove: async (data, actions) => {
const order = await actions.order.capture();
console.log(order);
},
onError: err => {
setError(err);
console.error(err);
}
})
.render(paypalRef.current);
}, [props.price]);
return (
<Row className="justify-content-center">
{error && <div>Uh oh, an error occurred! {error.message}</div>}
<div ref={paypalRef} />
</Row>
);
};
Everything is working except that a new button is created and added in the bottom of old one at each props change. I would like my new button to replace the old one.
You can pass the amount to the forceRerender property of the button and the button will rerender each whenever the amount is updated.
You should really just use react-paypal-button-v2
It updates with props, works as a stateless function and works with SSR such as next.js.
It even allows bypassing actions.order.create() so that you can call your own API's.
import { PayPalButton } from "react-paypal-button-v2";
const PaypalButton = ({total, cart}) => {
return (
<PayPalButton
createOrder={(data, actions) => {
return fetch('/api/paypal/create-transaction', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
total: total,
cart: cart,
})
})
.then((response) => {return response.json()})
.then((data) => {return data.orderID})
.catch(error => console.log(error))
}}
onApprove={(data) => {
// Capture the funds from the transaction
return fetch('/api/paypal/capture-transaction', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ orderID: data.orderID })
})
.then((res) => { return res.json() })
.then((details) => {
if(details === 200){
console.log('success');
} else {
console.log('failure');
}
})
.catch(error => {
console.log(error)
})
}}
options={{
clientId: process.env.PAYPAL_CLIENT_ID
}}
/>
);
}
export default PaypalButton;

Resources