Reputation: 1160
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='' />;
}
}```
Upvotes: 5
Views: 3085
Reputation: 1926
Your image component does not handle the props correctly. Assigning prop values to class variables won't work as you would expect, as this code gets executed once (because it's inside the constructor).
constructor(props) {
super(props);
// The following causes trouble.
this.url = props.url;
this.size = props.size;
}
You could assign a state in the constructor like the following, but it doesn't solve your issue. You would need to update the state accordingly at some place. There are various lifecycle methods to choose from, a matching one would be: UNSAFE_componentWillReceiveProps
but as the name suggests I would not use it anymore.
constructor(props) {
super(props);
this.state = {
url = props.url,
size = props.size,
};
}
Basically you can simply solve your issue by passing in the props into your JSX and get rid of the constructor stuff, as you do not need any state calculation based on your input values as these values are a url and size.
import React from 'react';
export default class Image extends React.Component {
render() {
return <img src={this.props.url} style={{ height: this.props.size + 'px' }} alt='' />;
}
}
Upvotes: 0
Reputation: 59541
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.
Upvotes: 3
Reputation: 282000
You don't need to assign the props value to class variable in the child component. Since you are doing that in the constructor it isn't getting updated.
Change your Image component to directly use the data from props
import React from 'react';
export default class Image extends React.Component {
//console.log(props.size);
constructor(props) {
super(props);
}
render() {
return <img src={this.props.url} style={{ height: this.props.size + 'px' }} alt='' />;
}
}
Upvotes: 3