I am fetching data using promise and setting the data using useState as shown below. How can use async/await instead of promise then?
// App
import React, { useState } from 'react';
import fetchEmails from 'data/fetchEmails';
const App = () => {
const [date, setDate] = useState('');
const [data, setData] = useState([]);
return (
<div>
<div>
<input
value={date}
onChange={setDate}}
/>
<button onClick={() => fetchEmails(date, setData)}>Get data</button>
</div>
<div>
{/* show data here, please ignore this part */}
{data.map(d => <div>{d.text}</div>)}
</div>
</div>
);
};
// fetchEmails
const fetchEmails = (date, setData) => {
fetch(
`http://localhost:9000/?date=${date}`
)
.then((res) => res.json())
.then((res) => setData(res))
.catch((err) => console.log(err));
};
export default fetchEmails;
you can do like this
const fetchEmails = async (date, setData) => {
let res = await fetch(`http://localhost:9000/?date=${date}`);
console.log(res);
setData(res);
};
const [src, setSrc] = useState(null);
const getSrc = async () => {
const response = await fetch(`https://aws.random.cat/meow`);
const data = await response.json();
setSrc(data.file);
};
const onClick = () => getSrc();
return (
<div>
<img src={src} alt={src} />
<button onClick={onClick}>click</button>
</div>
);
const onClick = useCallback(() => getSrc())
return <button onClick={onClick}></button>
Related
i am using TMDB api to make a movie and series website and i tried to use pagination to show more movies in one session and got this error:
'.map is not a function'
if I take the map it receives the information from the api on the console but I can't show it on the screen without the map
here is the code:
import ReactPaginate from 'react-paginate';
const imageUrl = import.meta.env.VITE_URL_BACKGROUND;
import '../components/components-home/PaginationCss.css';
const moviesURL = import.meta.env.VITE_API;
const apiKey = import.meta.env.VITE_API_KEY;
const Home = () => {
const [items, setItems] = useState([]);
useEffect(() => {
const getComments = async () => {
const res = await fetch(`${moviesURL}popular?${apiKey}&page=1`);
const data = await res.json();
setItems(data.results);
};
getComments();
}, []);
console.log(items);
const fetchComments = async (currentPage) => {
const res = await fetch(
`${moviesURL}popular?${apiKey}&page=${currentPage}`
);
const data = await res.json();
return data;
};
const handlePageClick = async (data) => {
console.log(data.selected);
let currentPage = data.selected + 1;
const commentsFormServer = await fetchComments(currentPage);
setItems(commentsFormServer);
};
return (
<div className=''>
<div className=''>
{items.map((item) => {
return (
<div className=''>
<div className=''>
<div className=''>
<img src={imageUrl + item.poster_path} alt='' />
</div>
</div>
</div>
);
})}
</div>
<ReactPaginate
previousLabel={'<<'}
nextLabel={'>>'}
breakLabel={'...'}
pageCount={15}
marginPagesDisplayed={3}
pageRangeDisplayed={6}
onPageChange={handlePageClick}
containerClassName={'pagination'}
activeClassName={'active'}
/>
</div>
);
};
export default Home;
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>
);
}
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
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>
</>
);
};
I have to create component which fetch data with pagination and filters.
Filters are passed by props and if they changed, component should reset data and fetch it from page 0.
I have this:
const PaginationComponent = ({minPrice, maxPrice}) => {
const[page, setPage] = useState(null);
const[items, setItems] = useState([]);
const fetchMore = useCallback(() => {
setPage(prevState => prevState + 1);
}, []);
useEffect(() => {
if (page === null) {
setPage(0);
setItems([]);
} else {
get(page, minPrice, maxPrice)
.then(response => setItems(response));
}
}, [page, minPrice, maxPrice]);
useEffect(() => {
setPage(null);
},[minPrice, maxPrice]);
};
.. and there is a problem, because first useEffect depends on props, because I use them to filtering data and in second one I want to reset component. And as a result after changing props both useEffects run.
I don't have more ideas how to do it correctly.
In general the key here is to move page state up to the parent component and change the page to 0 whenever you change your filters. You can do it either with useState, or with useReducer.
The reason why it works with useState (i.e. there's only one rerender) is because React batches state changes in event handlers, if it didn't, you'd still end up with two API calls. CodeSandbox
const PaginationComponent = ({ page, minPrice, maxPrice, setPage }) => {
const [items, setItems] = useState([]);
useEffect(() => {
get(page, minPrice, maxPrice).then(response => setItems(response));
}, [page, minPrice, maxPrice]);
return (
<div>
{items.map(item => (
<div key={item.id}>
{item.id}, {item.name}, ${item.price}
</div>
))}
<div>Page: {page}</div>
<button onClick={() => setPage(v => v - 1)}>back</button>
<button onClick={() => setPage(v => v + 1)}>next</button>
</div>
);
};
const App = () => {
const [page, setPage] = useState(0);
const [minPrice, setMinPrice] = useState(25);
const [maxPrice, setMaxPrice] = useState(50);
return (
<div>
<div>
<label>Min price:</label>
<input
value={minPrice}
onChange={event => {
const { value } = event.target;
setMinPrice(parseInt(value, 10));
setPage(0);
}}
/>
</div>
<div>
<label>Max price:</label>
<input
value={maxPrice}
onChange={event => {
const { value } = event.target;
setMaxPrice(parseInt(value, 10));
setPage(0);
}}
/>
</div>
<PaginationComponent minPrice={minPrice} maxPrice={maxPrice} page={page} setPage={setPage} />
</div>
);
};
export default App;
The other solution is to use useReducer, which is more transparent, but also, as usual with reducers, a bit heavy on the boilerplate. This example behaves a bit differently, because there is a "set filters" button that makes the change to the state that is passed to the pagination component, a bit more "real life" scenario IMO. CodeSandbox
const PaginationComponent = ({ tableConfig, setPage }) => {
const [items, setItems] = useState([]);
useEffect(() => {
const { page, minPrice, maxPrice } = tableConfig;
get(page, minPrice, maxPrice).then(response => setItems(response));
}, [tableConfig]);
return (
<div>
{items.map(item => (
<div key={item.id}>
{item.id}, {item.name}, ${item.price}
</div>
))}
<div>Page: {tableConfig.page}</div>
<button onClick={() => setPage(v => v - 1)}>back</button>
<button onClick={() => setPage(v => v + 1)}>next</button>
</div>
);
};
const tableStateReducer = (state, action) => {
if (action.type === "setPage") {
return { ...state, page: action.page };
}
if (action.type === "setFilters") {
return { page: 0, minPrice: action.minPrice, maxPrice: action.maxPrice };
}
return state;
};
const App = () => {
const [tableState, dispatch] = useReducer(tableStateReducer, {
page: 0,
minPrice: 25,
maxPrice: 50
});
const [minPrice, setMinPrice] = useState(25);
const [maxPrice, setMaxPrice] = useState(50);
const setPage = useCallback(
page => {
if (typeof page === "function") {
dispatch({ type: "setPage", page: page(tableState.page) });
} else {
dispatch({ type: "setPage", page });
}
},
[tableState]
);
return (
<div>
<div>
<label>Min price:</label>
<input
value={minPrice}
onChange={event => {
const { value } = event.target;
setMinPrice(parseInt(value, 10));
}}
/>
</div>
<div>
<label>Max price:</label>
<input
value={maxPrice}
onChange={event => {
const { value } = event.target;
setMaxPrice(parseInt(value, 10));
}}
/>
</div>
<button
onClick={() => {
dispatch({ type: "setFilters", minPrice, maxPrice });
}}
>
Set filters
</button>
<PaginationComponent tableConfig={tableState} setPage={setPage} />
</div>
);
};
export default App;
You can use following
const fetchData = () => {
get(page, minPrice, maxPrice)
.then(response => setItems(response));
}
// Whenever page updated fetch new data
useEffect(() => {
fetchData();
}, [page]);
// Whenever filter updated reseting page
useEffect(() => {
const prevPage = page;
setPage(0);
if(prevPage === 0 ) {
fetchData();
}
},[minPrice, maxPrice]);