I'm building a React Firebase blog and I want to pass {color} from the ViewPosts component (child) to the App component (parent).
App looks like this:
function App() {
const [backgroundColor, setBackgroundColor] = useState("");
return (
<div className="app" style={{ background: backgroundColor }}>
<Router>
<Routes>
<Route exact path="/" element={<Login />} />
<Route exact path="/register" element={<Register />} />
<Route exact path="/reset" element={<Reset />} />
<Route exact path="/dashboard" element={<Dashboard />} />
<Route exact path="/view-posts" element={<ViewPosts setBackgroundColor={setBackgroundColor} />} />
<Route exact path="/create-post" element={<CreatePost />} />
<Route exact path="/edit-post/:id" element={<EditPost />} />
<Route exact path="/profile/:id" element={<Profile />} />
</Routes>
</Router>
</div>
);
}
export default App;
I tried passing the state {setBackgroundColor} to to the ViewPosts component.
This is what ViewPosts looks like:
const ViewPosts = props => {
const [posts, setPosts] = useState([]);
const [user, loading, error] = useAuthState(auth);
const [searchInput, setSearchInput] = useState("");
const [filteredPosts, setFilteredPosts] = useState([]);
const [color, setColor] = useState("");
return (
<Container>
<div>
<div>
<h5>Search for a post by keyword</h5>
<input
id="search-bar"
type="text"
value={searchInput}
onChange={handleSearchChange}
/>
</div>
<input
id="search-bar"
type="text"
value={color}
onChange={(e) => setColor(e.target.value)}
/>
<button
className="view-post-button"
onClick={() => props.setBackgroundColor(color)}
>
Change background
</button>
{sortPosts(filteredPosts).map(post => (
<div
className="single-post"
id={post.id}
key={post.id}
>
<h2
className="post-title"
>
{post.data.title}
</h2>
<h5>{convertTimestamp(post.data.created)}</h5>
<div
className="post-body"
dangerouslySetInnerHTML={createMarkup(post.data.body)}>
</div>
<img
className="post-image"
src={`${post.data.image}`} />
<br></br><br></br>
<Link to={"/edit-post/" post.id}>
<button
className="view-post-button"
>
Edit
</button>
</Link>
<button
className="view-post-button"
onClick={() => handleDuplicate(post.id)}
>
Duplicate
</button>
<button
className="view-post-button"
onClick={() => handleDelete(post.id)}
>
Delete
</button>
</div>
))}
</div>
</Container>
);
};
export default ViewPosts;
I deleted the functions from ViewPosts for the sake of brevity, as I'm only concerned about the following lines in ViewPosts:
<input
id="search-bar"
type="text"
value={color}
onChange={(e) => setColor(e.target.value)}
/>
<button
className="view-post-button"
onClick={() => props.setBackgroundColor(color)}
>
I keep getting the error: Uncaught TypeError: props.setBackgroundColor is not a function.
Can someone help me figure out how to pass {color} from ViewPosts to {setBackgroundColor} in App?
CodePudding user response:
I didn't get the TypeError
. You are on the right track and can definitely pass the setter function directly or use a handler.
I provided a couple working examples below by extending your sample code to create a minimal reproduction of the question. Hope that helps!
You can type a color in the input and hit Change color
to see the result.
Solution/Demo
Using a handler: https://codesandbox.io/s/lift-up-state-example-fk78go
import { useState } from "react";
const ViewPosts = (props) => {
const [color, setColor] = useState("");
return (
<div
style={{
display: "flex",
flexDirection: "column",
border: "2px solid gray",
padding: "8px"
}}
>
<div>ViewPosts</div>
<input
type="text"
value={color}
onChange={(e) => setColor(e.target.value)}
/>
<p>Color: {color}</p>
<button type="button" onClick={() => props.onColorChange(color)}>
Change color
</button>
</div>
);
};
export default function App() {
const [backgroundColor, setBackgroundColor] = useState("blue");
const handleColorChange = (color) => {
setBackgroundColor(color);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "8px",
alignItems: "center",
textAlign: "center",
border: "2px solid gray",
padding: "8px"
}}
>
<div>App</div>
<div
style={{
backgroundColor: `${backgroundColor}`,
height: "40px",
width: "40px"
}}
/>
<ViewPosts onColorChange={handleColorChange} />
</div>
);
}
Passing setter function as prop: https://codesandbox.io/s/lift-up-state-pass-setter-as-prop-6jnykp
import { useState } from "react";
const ViewPosts = (props) => {
const [color, setColor] = useState("");
return (
<div
style={{
display: "flex",
flexDirection: "column",
border: "2px solid gray",
padding: "8px"
}}
>
<div>ViewPosts</div>
<input
type="text"
value={color}
onChange={(e) => setColor(e.target.value)}
/>
<p>Color: {color}</p>
<button type="button" onClick={() => props.setBackgroundColor(color)}>
Change color
</button>
</div>
);
};
export default function App() {
const [backgroundColor, setBackgroundColor] = useState("blue");
return (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "8px",
alignItems: "center",
textAlign: "center",
border: "2px solid gray",
padding: "8px"
}}
>
<div>App</div>
<div
style={{
backgroundColor: `${backgroundColor}`,
height: "40px",
width: "40px"
}}
/>
<ViewPosts setBackgroundColor={setBackgroundColor} />
</div>
);
}
Relevant Links/Resources
CodePudding user response:
Everything seems to be correct.
I think ViewPosts might have been used somewhere else without passing setBackgroundColor, and with just adding question mark or if condition to check if exist, your problem will be solved.
onClick={() => props?.setBackgroundColor(color)}
or
onClick={() => {
if(props.setBackgroundColor)
props.setBackgroundColor(color)
}}