I'm trying to render a background image using an object that's updated in response to an 'Enter' key after typing in a response. However, when I update my object (and that object is being used as an inline style), the background image isn't being rendered. It stays as what the image was before. Here's some relevant code:
function App() {
const [name, setName] = useState('');
const [headingText, setHeadingText] = useState('City');
let backStyle = {};
var dict = {
newyork: "https://images.unsplash.com/photo-1546436836-07a91091f160?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bmV3JTIweW9yayUyMGNpdHl8ZW58MHx8MHx8&w=1000&q=80",
chicago: "https://upload.wikimedia.org/wikipedia/commons/a/ab/Buildings-1804479_1920.jpg"
};
function handleChange(event){
setName(event.target.value);
}
function handleKeyDown(event){
if(event.key === 'Enter'){
for(var key in dict){
if(key === name){
backStyle = {
backgroundImage: 'url(' dict[key] ')'
}
}
}
setHeadingText(name);
}
}
return (
<div className="App">
<header className="App-header" style={backStyle}>
<input value={name} onKeyDown={handleKeyDown} onChange={handleChange} type="search" placeholder="Search for cities"></input>
<h1>{headingText}</h1>
</header>
</div>
);
}
CodePudding user response:
In React, if you want any change in UI, you should re-render your component.
There are several ways to re-render the component, but in this case useState
would be enough.
const [backStyle, setBackStyle] = useState({});
function handleKeyDown(event) {
if (event.key === 'Enter') {
for (var key in dict) {
if (key === name) {
setBackStyle({ backgroundImage: 'url(' dict[key] ')' })
}
}
setHeadingText(name);
}
}
}
setState
would re-render the component, so the change will be applied.
CodePudding user response:
If you want to re-render the background image then you must use state. In React, you must make a change to a component's state or prop in order to re-render.
So, instead of using setting background image in backStyle, we would initialize state like:
const [image, setImage] = useState('pic.jpg');
And then refactor handleKeyDown to change state, like:
if(key === name) setImage('picture.jpg');
CodePudding user response:
Others have already given the answer but let me give you an explanation. The culprit is let backStyle = {};
. Inside your handleKeyDown function, you are updating the headingText state variable which triggers a render. However, when you're component renders let backStyle = {};
runs again, which means any value it was before is gone. When you do const [backStyle, setBackStyle] = useState({})
and use setBackStyle
to change your backstyle. The value will persist because it is captured in state. If you find this hard to grasp, I recommend you do some research on state in React and the useState hook. If you don't have a firm grasp on state, you're doomed to fail in React.
CodePudding user response:
You need to useState to keep track of the background changes so that it can re-render when the background is changed.
import { useState } from "react";
function App() {
const [name, setName] = useState("");
const [headingText, setHeadingText] = useState("City");
const [backStyle, setBackStyle] = useState({
backgroundImage: "",
});
var dict = {
newyork:
"https://images.unsplash.com/photo-1546436836-07a91091f160?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8bmV3JTIweW9yayUyMGNpdHl8ZW58MHx8MHx8&w=1000&q=80",
chicago:
"https://upload.wikimedia.org/wikipedia/commons/a/ab/Buildings-1804479_1920.jpg",
};
function handleChange(event) {
setName(event.target.value);
}
function handleKeyDown(event) {
if (event.key === "Enter") {
for (var key in dict) {
if (key === name) {
setBackStyle({
backgroundImage: "url(" dict[key] ")",
});
}
}
setHeadingText(name);
}
}
return (
<div className="App">
<header
className="App-header"
style={{
backgroundImage: `${backStyle.backgroundImage}`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
height: "500px",
}}
>
<input
value={name}
onKeyDown={handleKeyDown}
onChange={handleChange}
type="search"
placeholder="Search for cities"
></input>
<h1>{headingText}</h1>
</header>
</div>
);
}
This should work.