I'm consuming the data from this api, using useEffect.
What I want, is that every time the data changes in the API. Change component information
const [infoUser, setInfoUser] = useState([]);
const getInfoUser = async () => {
try {
const accessToken = await AsyncStorage.getItem('#access_token');
axios
.get(
'https://apiexample.com/cliete',
{
headers: { Authorization: `Bearer ${accessToken}` },
},
)
.then(function (response) {
// handle success
console.log('DADOS USER:', response.data.data);
const userData = response.data.data;
setInfoUser([...infoUser, userData]);
})
.catch(function (error) {
// handle error
console.log(error);
});
} catch (e) {
// error reading value
console.log('Erro de token', e);
}
};
useEffect(() => {
getInfoUser();
}, []);
On another screen, I change the data and save.
But the previous screen has not been updated.
The data only changes when I log out and enter the app again
{infoUser.map(user => (
<Text style={styles.subtitle}>Nome Completo</Text>
<Text style={styles.title}>{user.nomeCompleto}</Text>
</TouchableOpacity>
))}
If you pass variables/functions to useEffect array of dependencies, and they are changing, it will trigger your useEffect. So if you want to depend on some data you can write it like this:
useEffect(() => {
getInfoUser();
}, [data]);
Every time data is updated useEffect will be called by React.
Related
I am pulling data from a cyrpto API that loads data of 250 coins. When I pull only 100 coins, the data is rendered fine. When I see it to 250, the data is rendered before loaded and it gives an error. The data is loaded in console when I log 250 coins.
The data function:
const fetchCoinData = async () => {
setLoading(true);
const fetchedCoinData = await getCoinsData();
setData(fetchedCoinData);
setLoading(false);
};
useEffect(() => {
fetchCoinData();
}, []);
The API call:
export const getCoinsData = async () => {
try {
const response = await Axios.get(
`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&per_page=100&page=1&sparkline=false&price_change_percentage=1h%2C24h%2C7d`
);
return response.data;
} catch (e) {
console.log(e);
}
};
It would help if you wrapped your axios response in a promise and change your state in the then function since it seems like your state is updating before your API call is over. Something like the followng would help.
await getCoinsData()
.then(fetchedCoinData => {
setData(fetchedCoinData))
setLoading(false)
});
I am consuming the data from this API, using useEffect. What works only the first time you open the component.
What I want, is that every time the data changes in the API. Change the component information.
When I use useIsFocused or useFocusEffect, I get the error: couldn't find a navigation object. is your component inside a screen in a navigator.
const [infoUser, setInfoUser] = useState([]);
const getNameUser = async () => {
try {
const accessToken = await AsyncStorage.getItem('#access_token');
/* console.log('Token', accessToken); */
axios
.get(
'https://exampleapi/api/cliente',
{
headers: { Authorization: `Bearer ${accessToken}` },
},
)
.then(function (response) {
// handle success
console.log('DADOS USER:', response.data.data.nomeCompleto);
const userData = response.data.data;
setInfoUser([userData]);
})
.catch(function (error) {
// handle error
console.log(error);
});
} catch (e) {
// error reading value
console.log('Erro de token', e);
}
};
useEffect(() => {
getNameUser();
}, []);
Using useEffect hook by passing empty array like this:
useEffect(() => {
getNameUser();
}, []);
Giving it an empty array acts like componentDidMount as in, it only runs once.
Giving it no second argument acts as both componentDidMount and
componentDidUpdate, as in it runs first on mount and then on every re-render
for example:
useEffect(() => {
getNameUser();
});
Does anyone know why this fetch continues to fire. I have also tried putting it inside a useEffect with no luck. It should only fire once to return once imdbID has loaded.
const WatchOnList = ({ imdbId }) => {
const [locations, setLocations] = useState([])
var headers = new Headers();
headers.append("x-api-key", "API_KEY")
var requestOptions = {
method: 'GET',
headers: headers,
crossDomain: true,
redirect: 'follow'
};
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then((res) => {
setLocations(res)
console.log(locations)
})
.catch(error => console.log('error', error));
}
fetchData();
With the current structure, the request will fire on every re-render. Which will be quite often in a React app. useEffect is the right place for such a function. But there are some caveats:
You can't make useEffect async, you have to create an async function inside the hook instead and call it afterward.
useEffect will per default run on every update, so you have to tell it explicitly to only run once (like componentDidMount for class components). This can be done by passing an empty array as the second parameter. The hook watches parameters specified in this array and only updates when one of them changes. As it is empty, it only fires once on initialization.
This should work:
useEffect(() => {
async function fetchData() {
const res = await fetch(`${awsApiUrl}?imdb_id=${imdbId}`, requestOptions);
res
.json()
.then(res => {
setLocations(res);
console.log(locations);
})
.catch(error => console.log("error", error));
}
fetchData();
}, []);
Read more about the behavior of hooks here and here.
I am using SWR react hook from nextJS with onSuccess callback function but it is not working as I expected. onsuccess callback is invoked but it is not receiving any data.
Here is the minimal code showing the issue:
pages/index.js
import useSWR from "swr";
export default function index() {
let data = { FirstName: "Default", LastName: "Default" },
error;
const fetcher = url =>
fetch(url).then(r => {
r.json();
});
const [shouldFetch, setShouldFetch] = React.useState(false);
useSWR(shouldFetch ? "/api/data" : null, fetcher, {
onSuccess: (data, key, config) => {
console.log({ data }); //this always prints "undefined"
this.data = data;
this.error = error;
}
});
function handleClick() {
setShouldFetch(true);
}
return (
<>
<button onClick={e => handleClick()}>Fetch</button>
Data retrieved from server is: {JSON.stringify(data)}
Error received from server is: {JSON.stringify(error)}
</>
);
}
pages/api/data.js:
export default (req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json");
res.send({ FirstName: "John", LastName: "Doe" });
};
When I go to localhost:3000/index and click on Fetch button, it still displays FirstName and LasName as "Default".
I think there is something wrong with my onSuccess callback function.
Here is the codesanbdbox demo: https://codesandbox.io/s/nervous-hill-mn0kz?file=/pages/index.js
What you need to do is return the data from the fetcher function
const fetcher = url =>
fetch(url).then(r => {
return r.json();
});
Also I would suggest refactoring the code by following best practices. You should ideally be wrapping the following inside a function.
useSWR(shouldFetch ? "/api/data" : null, fetcher, {
onSuccess: (data, key, config) => {
console.log({ data }); //this always prints "undefined"
this.data = data;
this.error = error;
}
});
to prevent running it everytime react renders. I would also suggest moving the data into a react state instead of having shouldFetch in state.
I don't know what does cause this, it sends new request almost every half a second. I was thinking it's because I call my action in render method but it's not, tried to call it in componentDidMount, the same result.
Here is the code:
Action:
export const getComments = () => dispatch => {
dispatch({
type: GET_COMMENTS
})
fetch(`${API_URL}/comments`,
{ method: 'GET', headers: {
'content-type': 'application/json'
}})
.then((res) => res.json())
.then((data) => dispatch({
type: GET_COMMENTS_SUCCESS,
payload: data
}))
.catch((err) => dispatch({
type: GET_COMMENTS_FAILED,
payload: err
}))
}
Since I need post id loaded before I call the comment action I put it in render method:
componentDidMount() {
const { match: { params }, post} = this.props
this.props.getPost(params.id);
}
render() {
const { post, comments } = this.props;
{post && this.props.getComments()}
return <div>
...
Here is the route:
router.get("/comments", (req, res) => {
Comment.find({})
.populate("author")
.exec((err, comments) => {
if (err) throw err;
else {
res.json(comments);
}
});
});
Your getComments() function is running during render. The dispatch used in the action is causing a re-render, causing getComments() to fire again, producing an infinite loop.
Instead of fetching comments in the render() function, you should instead fetch them in the componentDidMount lifecycle hook, then in the render function simply display the comments from props;
getComments() is invoking the http request, so it should be moved to componentDidMount lifecycle hoook.
This should work:
componentDidMount() {
const { match: { params } = this.props
this.props.getPost(params.id);
this.props.getComments()
}
render() {
const { post, comments } = this.props;
{post && comments}
return <div>
...
When the component has mounted, the params are retrieved from props.match and the Post and Comments are fetched. Then with redux, post and comments data is dispatched, and can be accessed in the connected component's render method.