useEffect keep fetching my data if I add state in useEffect - reactjs

when I added eventList into my useEffect in an array like this:
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, [eventList]);
It just keep fetching data non stop
but if I don't put eventList in there my page just fetch it one time and when I click to another component is will gone
Added Component:
Calendar.js:
const Calendar = (props) => {
const { teamId } = props;
const events = [];
const [date, setDate] = useState(new Date());
const [eventList, setEventList] = useState([]);
const [expanded, setExpanded] = useState("");
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, []);
const handleChangeAccordion = (panel) => (event, newExpanded) => {
setExpanded(newExpanded ? panel : false);
};
const handleChangeCalendar = (value) => {
const currentDate = moment(value).format("YYYY-MM-DD");
setDate(currentDate);
const currentEvents = events.find((event) =>
moment(currentDate).isSame(event.date, "day")
);
setEventList(currentEvents ? currentEvents.events : []);
};
const [showCalendarCard, setShowCalendarCard] = useState(false);
const addEvent = () => {
setShowCalendarCard(true);
};
return (
<div className="calendar-tab">
<div className="event-view-container">
<div className="event-date">
<p className="event-date-monthday">{moment(date).format("D")}</p>
<p className="event-date-weekday">{moment(date).format("dddd")}</p>
</div>
<div className="event-list">
{eventList.map((event, index) => (
<div>
<Accordion
key={`event-${index}`}
square
expanded={expanded === `event${index + 1}`}
onChange={handleChangeAccordion(`event${index + 1}`)}
>
<AccordionSummary>
<div className="event-list-item-header">
<span className="timestart">
{moment(event.timestart, "HH:mm:ss").format("h:mm A")}
</span>
<span className="dash">-</span>
<span className="timeend">
{moment(event.timeend, "HH:mm:ss").format("h:mm A")}
</span>
<span className="title">{event.title}</span>
</div>
</AccordionSummary>
</Accordion>
<div className="event-list-item-content">
<div className="header">
<span className="announcements">Announcements</span>
<div className="plus">
<ControlPoint />
</div>
</div>
<div className="content">{event.description}</div>
</div>
</div>
))}
</div>
</div>
<div className="calendar-view-container">
<div className="event-calendar-container">
{!showCalendarCard ? (
<div>
<EventCalendar
className="event-calendar"
formatShortWeekday={(locale, date) =>
moment(date).format("dd").charAt(0)
}
tileClassName={({ date }) => {
if (events.find((x) => moment(x.date).isSame(date, "day"))) {
return "highlight";
}
}}
onChange={(value) => handleChangeCalendar(value)}
nextLabel={<NavigateNext />}
prevLabel={<NavigateBefore />}
/>
<div className="add-event">
<ControlPoint onClick={addEvent} />
</div>
</div>
) : (
<CalendarCard
setShowCalendarCard={setShowCalendarCard}
teamId={teamId}
/>
)}
</div>
</div>
</div>
);
};
export default Calendar;
When I click to another day in calendar it will disspear my list of events.
Here is the photo of my project:

Your useEffect is depending on eventList to change and by calling that function you are changing eventList, If you want to send the request only once then this solution will do
const Calendar = (props) => {
const { teamId } = props;
// const events = [];
const [date, setDate] = useState(new Date());
const [eventList, setEventList] = useState([]);
const [expanded, setExpanded] = useState("");
const getEvents = async () => {
const res = await axios.get(`/api/v1/events/event/${teamId}`);
setEventList(res.data.Events);
};
useEffect(() => {
getEvents();
}, []);
Having an empty dependant list will only trigger the function once so change
useEffect(() => {
getEvents();
}, [eventList]);
To this
useEffect(() => {
getEvents();
}, []);

Its stuck in a loop because your useEffect callback has the side-effect changing it's own dependency (eventList).
Basically instead of resetting the event list you fetched from the API, memoize the filtered list
e.g.
const filteredEventList = useMemo(() => {
return eventList.filter((event) =>
moment(date).isSame(event.date, "day")
)
}, [date, eventList])
and then use this filtered event list in your render

Related

After clicking add icon in one image model all images in that row gets clicked //ReactJs

I want to add the images to mylist component by clicking the add icon but after clicking the add icon on image model(appears after clicking the image) the icon on all images of that row gets clicked.How to make the icon of that particular image model(appears after clicking the image) only clicked .
Actually i want to use firestore to store all the movies that are clicked (added) in the mylist component.
The code is below :-
function Slider({ rowtitle, type }) {
let img_path = "https://image.tmdb.org/t/p/w500";
let base_url = "https://api.themoviedb.org/3";
let url = base_url + type;
const [movieData, setData] = useState([]);
const [url_set, setUrl] = useState(url);
useEffect(() => {
fetch(url_set)
.then((res) => res.json())
.then((data) => {
setData(data.results);
});
}, [url_set]);
const [isOpen, setIsOpen] = useState(false);
const [title, setTitle] = useState();
const [overview, setOverview] = useState();
const [release, setRelease] = useState();
const [poster, setPoster] = useState();
const [backdrop, setBackdrop] = useState();
const [idd, setIDD] = useState();
const handleClicked = (movie) => {
setTitle(movie?.name || movie?.original_title);
setOverview(movie?.overview);
setRelease(movie?.release_date || movie?.first_air_date);
setPoster(movie?.poster_path);
setBackdrop(movie?.backdrop_path);
setIDD(movie?.id);
};
const [like, setLike] = useState(false);
const [DisLike, setDisLike] = useState(false);
const togglelike = () => {
if (DisLike) {
setDisLike(false);
setLike(true);
} else {
setLike(!like);
}
};
const toggledislike = () => {
if (like) {
setLike(false);
setDisLike(true);
} else {
setDisLike(!DisLike);
}
};
const [add, setAdd] = useState(false);
const [saved, setSaved] = useState(false);
const { user } = useUserAuth();
const movieID = doc(db, "users", `${user?.email}`);
const saveShow = async () => {
if (user?.email) {
setAdd(!add);
setSaved(true);
await updateDoc(movieID, {
savedShows: arrayUnion({
id: idd,
title: title,
img: backdrop,
}),
});
} else {
alert("please sign in to add movies to list");
}
};
const handleADD = () => {
saveShow();
};
return (
<div className="slidermain">
<div className="slidertitle">{rowtitle}</div>
<div className="slider">
**For popup model after clicking image**
<Modal className="popup" open={isOpen} onClose={() => setIsOpen(false)}>
<div className="modalinside">
<div className="modalcontent">
<div className="modaltitle">
<h1>{title}</h1>
</div>
<div className="modaldesc">{overview}</div>
<div className="modalicons">
<BiPlay className="modalicon" />
<p onClick={handleADD}>
<AiOutlinePlus
className={add ? "modalicon clickmodalicon" : "modalicon"}
/>
</p>
<BiLike
className={like ? " modaliconclick" : "modalicon"}
onClick={togglelike}
/>
<BiDislike
className={DisLike ? " modaliconclick" : "modalicon"}
onClick={toggledislike}
/>
</div>
<div className="modalrelease"> Release : {release}</div>
</div>
<div className="modalimg">
<img src={img_path + poster || backdrop} alt="poster" />
</div>
</div>
</Modal>
** For List of images ***
<div className="mainslider">
{movieData.map((res, pos) => {
return (
<div onClick={() => handleClicked(res)}>
<img
src={img_path + res.poster_path}
onClick={() => setIsOpen(true)}
alt="poster"
key={idd}
onError={(e) => (e.target.style.display = "none")}
/>
</div>
);
})}
</div>
</div>
</div>
);
}

Todo fetcher and Filter

I'm having difficulty in filtering the my todo-data according to the checkbox value. By default checkbox is checked which shows all data from the response. when Show Completed is checked alone it should only display the completed items, similarly for Show Incompleted checkbox.
export const Todo = () => {
const [todo, setTodo] = useState([]);
const [loading, setloading] = useState(false);
//fetched data
async function fetchData() {
setloading(true);
const data = await fetch("https://jsonplaceholder.typicode.com/todos ");
let res = await data.json();
res = res.splice(0, 20);
setTodo(res);
setloading(false);
}
useEffect(() => {
fetchData();
}, []);
//handle onChange
const compCheck = (e) => {
};
const InCompCheck = (e) => {
};
return (
<>
{loading && (
<h1>
<Loader />
</h1>
)}
{!loading && (
<>
<TodoItems todo={todo} loading={loading} />
<div id="filter-holder">
<label>Show Completed</label>
<input
id="completed-checkbox"
type="checkbox"
onChange={compCheck}
checked //by default should be checked to show complete list
/>
<br />
<label>Show Incompleted</label>
<input
id="incompleted-checkbox"
type="checkbox"
onChange={InCompCheck}
checked
/>
</div>
</>
)}
</>
);
};
this is sandbox link: https://codesandbox.io/s/todo-fetcher-and-filter-rci40?file=/src/Todo.js
Create a state for two checkboxes, update the state on change listener and then filter the todo array based on the checkbox state.
export const Todo = () => {
const [todo, setTodo] = useState([]);
const [loading, setloading] = useState(false);
const [checked, setChecked] = useState({ complete: true, incomplete: true });
async function fetchData() {
setloading(true);
const data = await fetch("https://jsonplaceholder.typicode.com/todos ");
let res = await data.json();
res = res.splice(0, 20);
setTodo(res);
setloading(false);
}
useEffect(() => {
fetchData();
}, []);
const getFilteredTodo = () => {
//if both are unchecked show nothing
if (!checked.complete && !checked.incomplete) return [];
return todo.filter((obj) => {
//if both are checked show all todo
if (checked.complete && checked.incomplete) return obj;
//filter objects based on the `Show completed` checkbox state
return checked.complete ? obj.completed: !obj.completed
});
};
const compCheck = (e) => {
setChecked((curr) => ({ ...curr, complete: e.target.checked }));
};
const InCompCheck = (e) => {
setChecked((curr) => ({ ...curr, incomplete: e.target.checked }));
};
return (
<>
{loading && (
<h1>
<Loader />
</h1>
)}
{!loading && (
<>
<TodoItems todo={getFilteredTodo()} loading={loading} />
<div id="filter-holder">
<label>Show Completed</label>
<input
id="completed-checkbox"
type="checkbox"
onChange={compCheck}
checked={checked.complete}
/>
<br />
<label>Show Incompleted</label>
<input
id="incompleted-checkbox"
type="checkbox"
onChange={InCompCheck}
checked={checked.incomplete}
/>
</div>
</>
)}
</>
);
};
First thing you need to do is controll your checkbox value and store in state.
Then write a filter function that filters the original TODO list. Then call the function in the component so that it executes itself with every state update and then loop through its result to render it.
const filterTodos = (todos, showCompleted, showIncompleted) => {
let filteredList = todos;
if (!showCompleted)
filteredList = filteredList.filter((todo) => !todo.completed);
if (!showIncompleted)
filteredList = filteredList.filter((todo) => todo.completed);
return filteredList;
};
const TodoList = () => {
const [todos, setTodos] = useState([]);
const [loading, setLoading] = useState(true);
//Set the initial values in state not in the input props
const [showCompleted, setShowCompleted] = useState(true);
const [showIncompleted, setShowIncompleted] = useState(true);
const onCompletedChangeHandler = (e) => {
setShowCompleted(e.target.checked);
};
const onInCompletedChangeHandler = (e) => {
setShowIncompleted(e.target.checked);
};
useEffect(() => {
async function fetchData() {
setLoading(true);
const data = await fetch("https://jsonplaceholder.typicode.com/todos ");
let res = await data.json();
res = res.splice(0, 20);
setTodos(res);
setLoading(false);
}
fetchData();
}, []);
return (
<>
{loading && (
<h1>
<Loader />
</h1>
)}
{!loading && (
<>
<TodoItems
todo={filterTodos(todos, showCompleted, showIncompleted)}
loading={loading}
/>
<div id="filter-holder">
<label>Show Completed</label>
<input
id="completed-checkbox"
type="checkbox"
onChange={onCompletedChangeHandler}
value={showCompleted}
/>
<br />
<label>Show Incompleted</label>
<input
id="incompleted-checkbox"
type="checkbox"
onChange={onInCompletedChangeHandler}
value={showIncompleted}
/>
</div>
</>
)}
</>
);
};

On click of button not filtering array like intended

Can someone explain to me why the filter method isn't working as intended? I am trying to update my array of objects with filtered results based on the first name.
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [inputField, setInputField] = useState("");
const searchUser = () => {
searchArray.filter((el) => el.firstName.includes(inputField));
};
useEffect(() => {
searchUser();
}, [inputField]);
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
<button onClick={(e) => searchUser()}> Search </button>
{searchArray.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}
You need to create a state which will contain the list of the users. Then, you want to update this state when inputField state changes.
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [users, setUsers] = useState([])
const [inputField, setInputField] = useState("");
useEffect(() => {
setUsers(data);
}, [data]);
useEffect(() => {
const filteredArray = users.filter((el) => el.firstName.includes(inputField));
setUsers(filteredArray)
}, [inputField]);
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
{users.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}
Or if you want to update the list of users by clicking a button:
export default function App() {
const { data } = useDataFetcher();
const searchArray = data;
const [users, setUsers] = useState([])
const [inputField, setInputField] = useState("");
useEffect(() => {
setUsers(data);
}, [data]);
const searchUser = () => {
const filteredArray = users.filter((el) => el.firstName.includes(inputField));
setUsers(filteredArray)
};
return (
<div className="App">
<h1>Xaxis Frontend Interview</h1>
<input onChange={(e) => setInputField(e.target.value)} />
<button onClick={() => searchUser()}> Search </button>
{users.map((userData, i) => (
<Users
key={i}
firstName={userData.firstName}
lastName={userData.lastName}
email={userData.email}
bio={userData.bio}
/>
))}
</div>
);
}

how can I handle variables reacts as variable - property?

I am trying to organize my code order to handle feed as feed.* based on my endpoint API, but however react doesn't allow me to directly send functions into component, but I want something similar to feed.results, feed. count
const [initialized, setIntialized] = useState(false);
const [feed, setFeed] = useState([]);
const browserFeed = async () => {
const response = await browse();
setFeed(response.results);
setIntialized(true);
};
useEffect(() => {
if (!initialized) {
browserFeed();
}
});
export const browse = () => {
return api.get('xxxxxxxx')
.then(function(response){
return response.data // returns .count , .next, .previous, and .results
})
.catch(function(error){
console.log(error);
});
}
<div className="searched-jobs">
<div className="searched-bar">
<div className="searched-show">Showing {feed.count}</div>
<div className="searched-sort">Sort by: <span className="post-time">Newest Post </span><span className="menu-icon">▼</span></div>
</div>
<div className="job-overview">
<div className="job-overview-cards">
<FeedsList feeds={feed} />
<div class="job-card-buttons">
<button class="search-buttons card-buttons-msg">Back</button>
<button class="search-buttons card-buttons">Next</button>
</div>
</div>
</div>
</div>
If it is pagination you are trying to handle here is one solution:
async function fetchFeed(page) {
return api.get(`https://example.com/feed?page=${page}`);
}
const MyComponent = () => {
const [currentPage, setCurrentPage] = useState(1);
const [feed, setFeed] = useState([]);
// Fetch on first render
useEffect(() => {
fetchFeed(1).then((data) => setFeed(data));
}, []);
// Update feed if the user changes the page
useEffect(() => {
fetchFeed(currentPage).then((data) => setFeed(data));
}, [currentPage]);
const isFirstPage = currentPage === 1;
return (
<>
<FeedsList feeds={feed} />
{isFirstPage && (
<button onClick={() => setCurrentPage(currentPage - 1)}>Back</button>
)}
<button Click={() => setCurrentPage(currentPage + 1)}>Next</button>
</>
);
};

How to passing functions to components?

I am doing the implementation of list pagination through a custom hook. The handleSetCurrentPage() function gets the correct number, it uses setCurrentPage(number). Consolelog setCurrentPage(number) showed undefined.
if you do all the same code only within one file (put everything in ListOfItems) it works fine.
Hook:
export const usePagination = (users = [], defaultPage = 1, amountPerPage = 10) => {
const [currentPage, setCurrentPage] = useState(defaultPage);
const [currentUsers, setCurrentUsers] = useState([]);
const [amountOfPages, setAmountOfPages] = useState(0);
useEffect(() => {
updateUsers();
updateAmountOfPages();
}, []);
const updateUsers = () => {
const indexOfLastPost = currentPage * amountPerPage;
const indexOfFirstPost = indexOfLastPost - amountPerPage;
const updatedUsers = users.slice(indexOfFirstPost, indexOfLastPost);
setCurrentUsers(updatedUsers);
};
const updateAmountOfPages = () => {
const updatedAmount = Math.ceil(users.length / amountPerPage);
setAmountOfPages(updatedAmount);
};
return {
setCurrentPage,
amountOfPages,
currentUsers,
};
};
list of items:
export function ListOfItems() {
const users = useSelector(state => state);
const { setCurrentPage, currentUsers, amountOfPages } = usePagination(users);
let {url} = useRouteMatch();
let items = currentUsers.map(function (value, index) {
return (
<form key={index}>
<div className="input-group">
<div className="input-group-prepend">
<Link className="input-group-text" to={`${url}/${index}`}>
{value.name}, {index}
</Link>
</div>
</div>
</form>
)
});
return (
<div>
{/*<form className="card">*/}
{/* <Search setSearch={setSearch} />*/}
{/*</form>*/}
<div>{items}</div>
<div>
<Pagination amountOfPages={amountOfPages} setCurrentPage={setCurrentPage}/>
</div>
</div>
)
}
pagination component:
const Pagination = ({amountOfPages, setCurrentPage}) => {
const [pageNumbers, setPageNumbers] = useState([]);
useEffect(() => {
calculatePageNumbers();
}, [amountOfPages]);
function calculatePageNumbers() {
const updatedPageNumbers = [];
for (let i = 1; i <= amountOfPages; i++) {
updatedPageNumbers.push(i);
}
setPageNumbers(updatedPageNumbers);
}
function handleSetCurrentPage(number) {
console.log(number);
return console.log(setCurrentPage(number));
}
return (
<nav>
<ul className="pagination">
{pageNumbers.map(number => (
<li key={number} className="page-item">
<button
onClick={() => handleSetCurrentPage(number)}
type="button"
className="page-link"
>
{number}
</button>
</li>
))}
</ul>
</nav>
);
};
export default Pagination;
useEffect(() => {
updateUsers();
updateAmountOfPages();
}, [currentPage]);

Resources