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();
});
Related
I have created a redux that is going to request an API and if the result is 200, I want to redirect the user to another page using history.
The problem is: I don't know how to trigger this change if the action is a success.
I could redirect the user in my useCase function but I can't use history.push pathName/state argument because it only works in a React component.
So this is what I have done in my React component:
const acceptProposalHandler = () => {
store.dispatch(acceptProposal(id)).then(() => {
setTimeout(() => {
if (isAccepted) { //isAccepted is false by default but is changed to true if the
//request is 200
history.push({
pathname: urls.proposal,
state: {
starterTab: formatMessage({id: 'proposalList.tabs.negotiation'}),
},
});
}
}, 3000);
});
};
Sometimes it works but other times it wont. For some reason, .then is called even if the request fails.
I'm using setTimeOut because if I don't, it will just skip the if statement because the redux hasn't updated the state with isAccepted yet.
This is my useCase function from redux:
export const acceptProposal = (id: string) => async (
dispatch: Dispatch<any>,
getState: () => RootState,
) => {
const {auth} = getState();
const data = {
proposalId: id,
};
dispatch(actions.acceptProposal());
try {
await API.put(`/propostas/change-proposal-status/`, data, {
headers: {
version: 'v1',
'Content-Type': 'application/json',
},
});
dispatch(actions.acceptProposalSuccess());
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
}
};
What I'm doing wrong? I'm using Redux with thunk but I'm not familiar with it.
".then is called even if the request fails." <- this is because acceptProposal is catching the API error and not re-throwing it. If an async function does not throw an error, it will resolve (i.e. call the .then). It can re-throw the error so callers will see an error:
export const acceptProposal = (id: string) => async (
// ... other code hidden
} catch (error) {
dispatch(actions.acceptProposalFailed(error));
// ADD: re-throw the error so the caller can use `.catch` or `try/catch`
throw error;
}
};
I'm trying to implement Redirect in my react js App so if API call returns status other that 200, I can redirect users to according pages. The problem is that Redirect doesn't work. My code so far:
function catchErr(res) {
try {
if (res.status === 200) {
return res.json();
} else if (res.status === 404) {
<Redirect to="/404" // doesn't redirect to this route
console.log("404") // prints 404
throw Error(res.status);
}
else {
if (res.ok) {
return res.data;
}
}
} catch (err) {
console.log(err);
}
}
export async function getData() {
let getParams = {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
};
const data = await fetch(url, obj)
.then((res) => catchErr(res))
.then((res) => {
return res.data;
});
return data;
}
My api call is a function in separate file, it's not react functional component so I can't use useState hook to store state or use useHistory(history.push) inside getData() function. At the moment api call works great, catchErr() console.loges 404 if res.status === 404 but doesn't redirect to "/404" as I need to. I can't figure out why Redirect wouldn't work in this case, please help.
You can't call JSX like that in the middle of a function. You'll need to refactor your code to handle the failures in a React component and either set some state to conditionally render a Redirect component into the DOM or access the history object to do an imperative redirect, i.e. history.replace.
Here's an example component using history.replace.
import { useHistory } from 'react-router-dom';
const MyComponent = () => {
const history = useHistory();
...
useEffect(() => {
const fetchData = async () => {
// set any loading state
try {
const response = await fetch(url, options);
// process response, throw error if STATUS 404
} catch(error) {
// handle any error responses and redirect
history.replace("/404");
} finally {
// clear any loading state
}
}
fetchData();
}, []);
...
return (
<div>My JSX</div>
)
};
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.
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 wanted to use a function as a react hook to wrap fetch requests to an API.
My current hook:
export function useAPI(url, options={}) {
const [auth, setAuth] = useGlobal('auth');
const [call, setCall] = useState(undefined);
const apiFetch = async () => {
const res = await fetch(url, {
...options,
});
if (!res.ok)
throw await res.json();
return await res.json();
};
function fetchFunction() {
fetch(url, {
...options,
});
}
useEffect(() => {
// Only set function if undefined, to prevent setting unnecessarily
if (call === undefined) {
setCall(fetchFunction);
//setCall(apiFetch);
}
}, [auth]);
return call
}
That way, in a react function, I could do the following...
export default function LayoutDash(props) {
const fetchData = useAPI('/api/groups/mine/'); // should return a function
useEffect(() => {
fetchData(); // call API on mount
}, []);
render(...stuff);
}
But it seems react isn't able to use functions in hooks like that. If I set call to fetchFunction, it returns undefined. If I set it to apiFetch, it executes and returns a promise instead of a function that I can call when I want to in the other component.
I initially went for react hooks because I can't use useGlobal outside react components/hooks. And I would need to have access to the reactn global variable auth to check if the access token is expired.
So what would be the best way to go about this? The end goal is being able to pass (url, options) to a function that will be a wrapper to a fetch request. (It checks if auth.access is expired, and if so, obtains a new access token first, then does the api call, otherwise it just does the API call). If there's another way I should go about this other than react hooks, I'd like to know.
Instead of putting your function into useState, consider using useCallback. Your code would look something like this:
export function useAPI(url, options={}) {
const [auth, setAuth] = useGlobal('auth');
function fetchFunction() {
fetch(url, {
...options,
});
}
const call = useCallback(fetchFunction, [auth]);
const apiFetch = async () => {
const res = await fetch(url, {
...options,
});
if (!res.ok)
throw await res.json();
return await res.json();
};
return call
}
The returned function is recreated whenever auth changes, therefore somewhat mimicking what you tried to do with useEffect