I'm new to react and I don't understand why the title inside the h1 tag gets updated, but the url inside the Image Component doesn't ?
import React, { useState, useEffect, useContext } from 'react';
import Image from './Image';
import Size from '../index';
export default function Listing(props) {
const [title, setTitle] = useState(props.title);
const [url, setUrl] = useState(props.url);
const value = useContext(Size);
return (
<div>
<Image url={url} size={value.size} />
<h1>{title}</h1>
<form>
<input id='URL' type='text' />
<button
onClick={e => {
e.preventDefault();
setUrl(document.getElementById('URL').value);
setTitle(document.getElementById('URL').value);
}}
>
Submit
</button>
</form>
</div>
);
}
My guess so far is that React wont update a child component if a prop changes, but how can I update that manually then ?
import React from 'react';
export default class Image extends React.Component {
//console.log(props.size);
constructor(props) {
super(props);
this.url = props.url;
this.size = props.size;
}
render() {
return <img src={this.url} style={{ height: this.size + 'px' }} alt='' />;
}
}```
setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.
If you want to set an initial value for the variable, pass the initial value as an argument to the useState function. When React first runs your component, useState will return the two-element array as usual but will assign the initial value to the first element of the array, as shown in figure 5.
To update the state, call the state updater function with the new state setState(newState) . Alternatively, if you need to update the state based on the previous state, supply a callback function setState(prevState => newState) .
TL;DR: useState is an asynchronous hook and it doesn't change the state immediately, it has to wait for the component to re-render. useRef is a synchronous hook that updates the state immediately and persists its value through the component's lifecycle, but it doesn't trigger a re-render.
There are many "smells" with your code. Here is the short, quick-fix answer:
Change this: src={this.url}
to this: src={this.props.url}
.
The reason the image never updated is because of this:
constructor(props) {
super(props);
this.url = props.url;
this.size = props.size;
}
You are setting local variables to be the initial prop values. Since you are setting these in your constructor, these lines will ever only be executed when your component is created, but never when new props are sent in. React is still triggering a re-render, since you are sending in new props, but the new value is never used, so the old result stays.
Slightly longer answer:
It's rarely a good idea to mix values read from the DOM directly like you do here:
setUrl(document.getElementById('URL').value);
setTitle(document.getElementById('URL').value);
Instead, have 2 states. One that holds the current value of your input which updates with every keystroke, and another that holds the value that you send down to your Image component.
Maybe something like:
export default function Listing(props) {
const [title, setTitle] = useState(props.title);
const [inputUrl, setInputUrl] = useState(props.url);
const [url, setUrl] = useState(props.url);
const value = useContext(Size);
return (
<div>
<Image url={url} size={value.size} />
<h1>{title}</h1>
<form>
<input
value={inputUrl}
onChange={e => setInputUrl(e.target.value)}
/>
<button
type="button"
onClick={() => {
setUrl(inputUrl);
setTitle(inputUrl);
}}
>
Submit
</button>
</form>
</div>
);
}
Notice also that I removed e.preventDefault()
and added type="button"
to your button, since the default type is submit
, which would probably refresh your page.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With