Reputation: 23593
My React app allows users to upload profile images. This is done by uploading the image to an Amazon S3 bucket and storing the path to the image in my database, along with the date and time the image was uploaded. The filename of the image is set to the user's ID.
Im having a problem when a user uploads a new image. As the image path is the same React doesn't know anything has changed, meaning I have to refresh the page to see the change.
As I have a date field I can use componentWillReceiveProps to know when a new image has been uploaded. The following console.log does fire at the correct time:
componentWillReceiveProps(nextProps) {
if (this.state.picDate.getTime() !== nextProps.user.pic.date.getTime()) {
console.log('image date has changed');
// this.forceUpdate();
}
}
Can I use this to re-render the image? I tried this.forceUpdate()
but it doesn't work.
Upvotes: 43
Views: 47360
Reputation: 21
The solution working for me is to add a bypassCache like this:
image.src = url + "?" + Math.random().toString(36);
Upvotes: 2
Reputation: 39
What worked for me was changing the image name with every new upload, for example:
const new_file_name = Date.now() . '/' . file_extension
This new file name needs to be stored on some database, relating it to the user. It's also a good idea that each user has a folder, to avoid that two users have the same image name.
When loading the app, request the user image name from the api and you will get the name of the file. As you know the user, you know in which folder to search for this.
In case you are uploading the file and want to see the image change immediately, store this file name into a react or redux state. The Image element will re-render on change.
To get the image from your api do something like:
<Image
source={{
uri: your_localhost_ip + ':' + your_port +'/'+ your_folder_containing_all_user_pics + '/' + user_id + '/' + user_picture,
cache:'reload' //only works for ios
}}
/>
Upvotes: 0
Reputation: 99
Better solution may be to change the structure of your code and API
Solution 1 Add a updatedDate to the object and use that. It needs to be a full Date value.
const bypassCache = new Date(updatedDate).getTime()
const imageUrl = `${image.url}?${bypassCache}`
// imageUrl = https://example.com/userId/imageId?16504372055
You'd need a value that's unique for the new entry compared to the previous. If you need to compare, you may use useRef to hold the initial value and then compare to the new updatedDate.
Solution 2
Store the bypassCache in the pathname itself https://example.com/userId/imageId/bypassCache
Thereby handling it from "backend". For the users privacy you may use a different unique value that cannot be parsed/reversed into a real timestamp.
Upvotes: 2
Reputation: 103
This works on functional components (I haven't tried it on class based components but it should work too):
const MyComponent = () => {
const [source, setSource] = useState('some_image.png');
const changeImage = (newSource) => {
setSource(null);
setTimeout(() => setSource(newSource));
};
return (
<img
src={source}
/>
);
};
Note: I have tried using random hash and also changing key, they didn't work. This might work for you
Upvotes: 0
Reputation: 51
Add current date as query parameter to the image url
<img src={`${image_url}?${global.Date.now()}`} />
Upvotes: 1
Reputation: 435
(Enhancement to Eric Wiener answer), set key (ie. timestamp) upon upload to s3 to get a cleaner refresh.
const [timestamp, setTimestamp] = React.useState(Date.now());
axios.post("/api/upload/s3", formData)
.then(res => {
setTimestamp(Date.now())
})
<Avatar key={timestamp} className={classes.avatar} src={user_info.avatar} onClick={handleOpenPicture}></Avatar>
Upvotes: 3
Reputation: 1979
You can try doing something like first set state to null so image wont display and again set state to particular image with path.
constructor(props){
super(props);
this.state = {
image_path : 'before update image path'
};
}
componentWillReceiveProps(nextProps) {
if (this.state.picDate.getTime() !== nextProps.user.pic.date.getTime()) {
console.log('image date has changed');
//now here set state
this.setState({
image_path : 'your new image path or older as you explained both are same' + '?' + Math.random()
});
}
}
Upvotes: 5
Reputation: 5947
I was having this issue with a local image. The image was being modified, but the URL was staying the same.
The trick was just to add a key property to the image that changed whenever the image changed. For instance:
<Image key={Date.now()} source={{uri: <your local uri>}/>
Upvotes: 20
Reputation: 6629
I think that's because of the browser cache. try adding some hash like date.now()
after image URL changes. for example:
setState({
imageSrc: '...',
imageHash: Date.now()
})
and in render:
render(){
return(
<img src={`${imageSrc}?${imageHash}`} />
)
}
Upvotes: 73
Reputation: 4957
Its working fine for me.
<img src={`${props.src}?${new Date().getTime()}`} />
Upvotes: 7
Reputation: 11
Just set image path blank and then set to whatever your path was, react will assume its a change and new pic will be loaded.
this.setState({image_path : ''});
this.setState({image_path : 'Actual Image Source'});
Like I am doing in my FileUpload
function
this.setState({userimage:''});
this.setState({userimage:"http://localhost:5000/auth/getProfilePic/" + this.state.username,});
Upvotes: 1