How do I trigger a rerender in react? - reactjs

The idea is that I select an image which then gets uploaded to mongodb and in the same time as I press on upload button I want to trigger rerendering of the page so that the avatar gets updated with the newest image. To accomplish that I first send a PUT request t the server and right after that I set a local storage obejct with the values of the response.
The functions that capture and upload the image
const [reload, setReload] = React.useState(false);
React.useEffect(() => {}, [reload])
const fileSelectedHandler = evt => setAvatarImage(evt.target.files[0]);
const uploadProfileImage = async evt => {
evt.preventDefault();
const formData = new FormData();
formData.append('image', avatarImage);
try {
const response = await axios({
method: 'PUT',
url: `${keys.SERVER_URL}/user/avatar/${isAuthenticated()._id}`,
data: formData,
headers: { 'Content-Type': 'multipart/form-data',}
});
localStorage.setItem(localStorageName, JSON.stringify({ avatar: response.data.user.avatar }));
} catch (error) {
console.log(error);
}
setReload(prevState => !prevState);
}
The thing is that the image is getting uploaded and is displayed correctly. The problem is that after I press on upload button the image is not shown instantly but I have to manually reload the page. Thats why I added that setReload. So that when I press on upload button the value of reload variable gets changed, that triggering a reload in useEffect.

The best practice is to use setState or something similar depending on your state management. localStorage does not trigger rerenders. React isn't designed that way.

Related

FullCalendar events (as a json feed) is getting re-fetched after eventClick and dayClick

I am using FullCalendar in a React page and in order to get the events I am using events as a json feed (https://fullcalendar.io/docs/events-json-feed). The data gets loaded fine however when I use eventClick or dateClick to open a modal, FullCalendar is refreshing and another POST request is sent to the backend.
Is there a way to prevent that? I want to avoid sending unnecessary requests...
Also, as the data gets refreshed the calendar events are re-drawn and this causes to look like a glitch. Similar to this:
https://user-images.githubusercontent.com/3365507/85287154-6fc61c00-b4c6-11ea-83c1-cb72a3aec944.gif
Here are a few examples of the code I am using:
<FullCalendar
...
eventClick={handleEventClick}
dateClick={handleDateClick}
eventSources={[
{
events: fetchEvents,
failure: function() {
console.log('ERROR');
}
},
]}
...
/>
And fetchEvents is something like this:
const fetchEvents = (fetchInfo, successCallback, failureCallback) => {
fetch('http://localhost/calendarEvents', {
method: 'POST',
body: JSON.stringify(fetchInfo),
headers: {
'Content-Type': 'application/json',
},
})
.then((res) => res.json())
.then((data) => {
const parsedEvents = [];
for (const event of data) {
parsedEvents.push({
...event,
start: moment(event.startAt).toDate(),
end: moment(event.endAt).toDate(),
title: event.title
});
}
successCallback(parsedEvents);
})
.catch((error) => {
failureCallback(error);
});
}
and handleEventClick:
const handleEventClick = (event) => {
setSelectedEvent(event);
setOpenEventModal(true);
};
--EDIT--
Here is a CodeSandbox example:
https://codesandbox.io/s/suspicious-murdock-4jfept?file=/src/App.js
You can see at the Console tab that a new fetch is tried each time you click at a date to open the Modal. A new fetch is expected only when switching months in the calendar because I am using eventSources json feed option. But if it was already fetched it shouldn't do it again just by opening the Modal.
setSelectedEvent(event);
setOpenEventModal(true);
If state changes in <FullCalendar> it will rerender. This may be causing it to call for the data again.
Either stop changing the state in FullCalendar, do your API calls outside and pass in the data, or don't call for the data on every render.
What is the full code for <FullCalendar>?

Redux toolkit RTK query mutation not getting returning data

Hi I recently learned the new react toolkit with the rtk query tool, and I am trying to put in a login system together using the createApi from the rtk package.
After giving it a test on the login button pressed, I see the network request going through without any issue(status code 200), and I get a response object providing user, token, however, when I try to get the returning data using useLoginMutation I get an undefined value.
below is the code for my endpoint which is injected in a base api:
export const apiLogin = theiaBaseApi.injectEndpoints({
endpoints: (build) => ({
loginUser: build.mutation<UserReadonly, loginValuesType | string>({
query: (values: loginValuesType, redirect?: string) => {
const { username, password } = values;
const header = gettingSomeHeaderHere
return {
url: "login",
method: "GET",
headers,
crossDomain: true,
responseType: "json",
};
},
}),
}),
});
export const { useLoginUserMutation } = apiLogin
then inside my React component I destructure the mutation result such like below:
const [login, {data, isLoading}] = useLoginUserMutation();
const submitLogin = () => {
// pass in username password from the form
login({username, password});
}
Suppose if I console log out data and isLoading I assume that I will see data: {user: "abc", token: "xyz"}, because under network tab of my inspect window I can see the response of this network request, but instead I am seeing data: undefined
Does any have experience on solving this?
Oh I found the reason, it was a very careless mistake. I had to wrap the reducer to my store, which was what I was missing
In my case the issue was that I was trying to access the UseMutationResult object inside onClick callback. And the object was not updating inside the callback, even though in the component the values were accurate.
If I put the log outside it's working just fine.
here is an example for better understanding (inside handleAddPost the mutationResult is not updating)
Here is a code sample (in case link is not working):
const Component = () => {
const [addPost, mutationResult] = useAddPostMutation();
...
const handleAddPost = async () => {
...
console.log("INSIDE CALLBACK isLoading and other data is not updating:");
console.log(JSON.parse(JSON.stringify(mutationResult)))
...
};
// in the example this is wrapped in an useEffect to limit the number of logs
console.log(mutationResult.data,"OUTSIDE CALLBACK isLoading and other data is working:")
console.log(JSON.parse(JSON.stringify(mutationResult)))
return (
...
<Button
...
onClick={handleAddPost}
>
Add Post
</Button>
...

Cannot show images after fetching from an API

I'm trying to chain two fetch request using axios. My code is :
const fetchCatsData = async () => {
const fetchBreeds = await axios.get("https://api.thecatapi.com/v1/breeds", {
headers: {
"x-api-key": "MY API KEY ",
},
})
await fetchBreeds.data.map(breed => {
axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
})
dispatch({ type: FETCH_BREEDS, payload: fetchBreeds.data })
It succeeds and in react dev tools , i see a special key called 'image_url'inside my context , with the url of the image.I click on it's value and it open the requested image.
But when i'm trying to show the image in an image HTML tag , it shows nothing ...
Am i missing something ?
Thanks in advance
From the code here, i guess you are trying to wait until the image_url is set for all items in fetchBreeds.data. But it won't work in that way.
await fetchBreeds.data.map(breed => {
axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
})
You use await on the map function. The map function is not async, so when you dispatch the action, the image_urls are not set yet. When you checked the store and found the image_url was there is because the fetchBreeds.data was mutated by axios call directly without using redux dispatch system. This didn't trigger the UI re-render so image didn't show. What happened is shown below:
dispatch({ type: FETCH_BREEDS, payload: fetchBreeds.data }) This happens first.
Component is notified so re-render. image_url is not set yet, so image is empty
.then(res => breed.image_url = res.data[0].url) This is called next. Because the function holds the reference of the fetchBreeds.data, so it changes the fetchBreeds.data object directly without using reducer.
UI is not notified so it doesn't know image_url is changed and won't re-render.
I suggest you change the function to:
await Promise.All(fetchBreeds.data.map(breed => {
return axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
}))

React Native Formik handleSubmit does not read return values from function

Good day!
Im having a weird experience using formik today,
I am currently working on an app that uses formik to handle forms and will do an API request inside the onSubmit() function of formik.
Every thing went well except when i use API request and wait for it's callback.
Somehow the things inside the function of onSubmit will work properly but the API callback value does not return unless i perform a UI Change in the app itself (like pressing random spots on my screen to trigger ui change).
Here is a look of my onSubmit function of formik
onSubmit={values => {
console.log("before")
let response = FunctionWithApiRequest(values);
console.log("after")
response.then((res) => {
console.log(res)
})
}}
and here is my function with api request inside
const FunctionWithApiRequest = (credentials) => {
return fetch(`${AppConfig.HOSTNAME}/v2/auth/signup`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials)
})
.then((response) => response.json())
.then((responseJson) => {
return responseJson
})
.catch((error) => {
console.log(error)
});
}
The return "responseJson" will only appear inside the onsubmit function when i perform a UI Change (like clicking random spots in my react native screen)
i was wondering what is the problem and what cause the bug.
Thank you for your response in advance.
Possibly you can do this in a separate function with await and async.For instance
async handleSubmit(){
let {credentials} = this.state
let data = await this.props.FunctionWithApiRequest(credentials)
this.setState({returnedData: data})
}
And now in your textInput/formik
onSubmit={()=>this.handleSubmit()}
I assume you have made the request api function in actions.file, not in the same file.Am i right?So what i have done is just put await before call.Which means next line will execute only when you have response returned.Comment down if you have any issue.
It was caused by the haul bundler, when you enable dugging mode.

Value of state variable is lost - React

I want to build a CRUD in React with Laravel and Firebase. Everything is perfect when I'm working with text, but I got trouble when I try to upload an image to Firebase Storage. I can save it but I can't get its URL.
I wrote 2 "console.log". In the first one the URL is there, but the second one (when I try to get the URL from the state variable) doesn't return anything.
handleSubmit = event =>{
event.preventDefault();
const {imagen} = this.state;
if(imagen!=null){
const uploadTask = storage.ref(`imagenes/${imagen.name}`).put(imagen);
uploadTask.on('state_changed',
(snapshot) => {
const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
this.setState({progress});
},
(error) => {
console.log(error);
},
() => {
storage.ref('imagenes').child(imagen.name).getDownloadURL().then(url => {
this.setState({url});
console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)
})
});
}
var direccion = null;
const form = event.target;
let data = new FormData(form);
data.append('url', this.state.url);
console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE)
If you want to check the entire file:
https://github.com/AndresVasquezPUCE/project/blob/master/pelicula
I'm not a professional, so please don't be rude :D
this.setState is asynchronous
If you want to get the updated state value, add a callback and access the new state there like
this.setState({ url: 'some url'}, () => {
conosle.log(this.state.url);
});
Data is loaded from Firebase asynchronously. By the time your console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE) the data hasn't been loaded from Firebase yet, and the then hasn't been called yet.
Any code that needs the data from Firebase needs to either be inside the then() callback (such as console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)) or be called from there (such as this.setState({url})).

Resources