Reputation: 331
I'm reading the answer of this question: Problem with conditional statements in React Hook how can I Rewrite this code with multiple use state? in this answer gather all the state objects to a single state.is there any way to rewrite this code with multiple and separate state?
const initialState = {
movies: [],
loading: true,
searchKeyword: "",
filterOverSeven: false,
filterByWatch: "ALL" // Can be "ALL" | "WATCHED" | "NOT_WATCHED"
};
const App = () => {
const [state, setState] = useState(initialState);
useEffect(() => {
fetch("https://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setState((s) => ({
...s,
movies: response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false
})),
loading: false
}));
});
}, []);
function handleWatchedBtn(id) {
setState((s) => ({
...s,
movies: s.movies.map((movie) => {
if (movie.id === id) {
return { ...movie, watched: !movie.watched };
}
return movie;
})
}));
}
function handleKeywordChange(e) {
setState((s) => ({ ...s, searchKeyword: e.target.value }));
}
function handleOverSevenChange(e) {
setState((s) => ({ ...s, filterOverSeven: !s.filterOverSeven }));
}
function handleWatchedChange(filter) {
setState((s) => ({ ...s, filterByWatch: filter }));
}
function filterItems() {
return state.movies
.filter((item) =>
item.name.toLowerCase().includes(state.searchKeyword.toLowerCase())
)
.filter((item) => (state.filterOverSeven ? item.rate > 7 : true))
.filter((item) =>
state.filterByWatch === "ALL"
? true
: item.watched === (state.filterByWatch === "WATCHED")
);
}
if (state.loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input
type="text"
value={state.searchKeyword}
onChange={handleKeywordChange}
/>
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>watch</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
not watch
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={state.filterOverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filterItems().map((movie) => (
<li data-id={movie.id}>
{`${movie.id} : ${movie.name} ${movie.rate}`}{" "}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "Watched" : " Not watched"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
this is my code and how I try to change that.
import React, { useEffect, useState } from "react";
import { getAllMovies } from "./components/Transportlayer";
const App = () => {
const [movies, setMovies] = useState([]);
const [Keyword, setKeyword] = useState("");
const [OverSeven, setOverSeven] = useState(false);
const [Loading, setLoading] = useState(true);
const [filterByWatch, setfilterByWatch] = useState("ALL");
useEffect(() => {
getAllMovies().then((response) => {
setMovies(
response.map((item) => ({
id: item.id,
name: item.name,
rate: item.rate,
watched: false,
}))
);
setLoading(false);
});
}, []);
function handleWatchedBtn(id) {
setMovies({
movies: movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
});
}
function handleKeywordChange(e) {
setKeyword(e.target.value);
}
function handleOverSevenChange(e) {
setOverSeven(e.target.checked);
}
function handleWatchedChange(filter) {
setfilterByWatch({ filterByWatch: filter });
}
function filterItems() {
return movies
.filter((item) =>
item.name.toLowerCase().includes(Keyword.toLowerCase())
)
.filter((item) => (OverSeven ? item.rate > 7 : true))
.filter((item) =>
filterByWatch === "ALL"
? true
: item.watched === (filterByWatch === "WATCHED")
);
}
if (Loading) {
return "Please wait...";
}
return (
<div>
<div>
<div>
Keyword
<input
type="text"
value={Keyword}
onChange={handleKeywordChange}
/>
</div>
<div>
<button onClick={() => handleWatchedChange("ALL")}>all</button>
<button onClick={() => handleWatchedChange("WATCHED")}>done</button>
<button onClick={() => handleWatchedChange("NOT_WATCHED")}>
undone
</button>
</div>
<div>
Only over 7.0
<input
type="checkbox"
checked={OverSeven}
onChange={handleOverSevenChange}
/>
</div>
<div>
<ul>
{filterItems().map((movie) => (
<li data-id={movie.id}>
{`${movie.name} ${movie.rate}`}
<button onClick={() => handleWatchedBtn(movie.id)}>
{movie.watched ? "done" : " undone"}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
export default App;
Upvotes: 1
Views: 1087
Reputation: 331
This is the answer given by @Luca Pizzini and work for this problem.
change button text and state by click and show list in react hooks
Upvotes: 0
Reputation: 305
This functions is wrong
function handleWatchedBtn(id) {
setMovies({
movies: movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
});
}
you are returning an object with key movies inside movies state, but movies should be an array of movies. Try something like this:
function handleWatchedBtn(id) {
setMovies(() => movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
)
});
}
Upvotes: 0
Reputation: 1802
In the second code (separated states), you define movies
as an array, but you are changing it to an object:
function handleWatchedBtn(id) {
setMovies({
movies: movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
});
}
I think this would work:
function handleWatchedBtn(id) {
setMovies(
movies.map((movie) => {
if (movie.id === id) {
return { movie, watched: !movie.watched };
}
return movie;
})
);
}
Same with the filterByWatch
state, its defined to be a string
but in here you are setting to it to a object:
function handleWatchedChange(filter) {
setfilterByWatch({ filterByWatch: filter });
}
correct:
function handleWatchedChange(value) {
setfilterByWatch(value);
}
Upvotes: 1