I wrote a program that takes a data that has an object from the movie and adds the state of the watch to each of the objects and puts it in the state. The whole program works properly My only problem is that when I click on the watched button, I want the text of the button to be changed to not watched and to be included in the watched list. At the top, I have two buttons, watched and not watched, which one I clicked on. Show me the list of the movies that I changed, their state In fact, every time I click on the button, I want the state of Watched in the object of that movie to change to false and click again to true. I specified in the code with a comment I know my questions are a bit confusing, but thank you for helping me
import React, { useEffect, useState } from "react"; const App = () => { const [getMovies, setMovie] = useState([]); const [getLoading, setLoading] = useState(true); const [getKeyword, setKeyword] = useState(""); const [getOverSeven, setOverSeven] = useState(false); const [getWatched, setWatched] = useState(true); const [getNotWatched, setNotWatched] = useState(false); const [getTextBtn, setTextBtn] = useState(false); useEffect(() => { fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies") .then((response) => response.json()) .then((response) => { setMovie( response.map((item) => ({ id: item.id, name: item.name, rate: item.rate, watched: false, })) ); setLoading(false); }); }, []); //This is the part that I want every time I click on the button of each movie // to change the button to not watched and in the state of watching state // to false and with the next click to become true function handleWatchedBtn(id) { setTextBtn(!getTextBtn); const index = getMovies.findIndex((p) => p.id === id); if (index) { if (getTextBtn === true) { setMovie({ id, name, rate, watched: true, }); } else { setMovie({ id, name, rate, watched: false, }); } } } function handleKeywordChange(e) { setKeyword(e.target.value); } function handleOverSevenChange(e) { setOverSeven(e.target.checked); } function handleShow() { setNotWatched(!getNotWatched); setWatched(!getWatched); } function filterItems(getKeyword, getOverSeven, getWatched, getNotWatched) { const arr = getMovies.map((item) => ({ id: item.id, text: item.name, rate: item.rate, watched: item.watched, })); return arr .filter((item) => item.text.toLowerCase().includes(getKeyword.toLowerCase()) ) .filter((item) => (getOverSeven ? item.rate > 7 : true)) .filter((item) => (getWatched ? item.watched === true : getNotWatched)) .filter((item) => (getNotWatched ? item.watched === false : getWatched)); } const result = filterItems( getKeyword, getOverSeven, getNotWatched, getWatched ); if (getLoading) { return "Please wait..."; } return ( <div> <div> <div> Keyword <input type="text" value={getKeyword} onChange={handleKeywordChange} /> </div> <div> <button onClick={handleShow}>watch</button> <button onClick={handleShow}>not watch</button> </div> <div> Only over 7.0 <input type="checkbox" checked={getOverSeven} onChange={handleOverSevenChange} /> </div> <div> <ul> {result.map((item) => ( <li data-id={item.id}> {`${item.id} : ${item.text} ${item.rate}`}{" "} <button onClick={handleWatchedBtn}> {getTextBtn ? "Not watched" : " Watched"} </button> </li> ))} </ul> </div> </div> </div> ); }; export default App;
Advertisement
Answer
Your state has too many useState
state objects. It’s better to move them all to a single state.
Next the problem with watched
and not watched
buttons not working is that you are not passing your movie id
to handleShow
function.
Also having two different booleans like getWatched
and getNotWached
to filter your movie list is not necessary and adds extra complexity.So move it to a single variable like filterByWatch
which can be "ALL" | "WATCHED" | "NOT_WATCHED"
. So you just have to change one variable while toggling -> fewer state updates
handleWatchedBtn
function also needs some change. You extracted the index of the movie that is clicked to toggle but you are not using that later in the function.
Here is an attempt to fix all the above-mentioned issues.
Please feel free to comment with any questions you have about the below code.
import React, { useEffect, useState } from "react"; 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;