I am implementing the paging function, click the next page to get the data again, but the function to get the data is a hook function, what should I do?
import React, { useRef, useEffect, useState, useCallback } from 'react';
import type { PaginationProps } from 'antd';
import { Pagination } from 'antd';
import { useGetArticlesQuery } from '../../store/api/articleApi'
const App = () => {
const onChange: PaginationProps['onChange'] = (page) => {
setCurrent(page);
// I want to get articles data through hook useGetArticlesQuery but fail
// const { data, isSuccess } = useGetArticlesQuery()
};
<Pagination current={current} onChange={onChange} total={total} defaultPageSize={amount} />
}
maybe you should rethink the architecture of your app.
try to use the base of the code below and adjust according to your needs
import React, { useEffect, useState } from 'react';
const App = () => {
useEffect(() => {
const options = //...
fetch('https://xxxxxxxx/api/getAllArticles/page=${page}', options)
.then((response) => response.json())
.then((data) => {
console.log('Success:', data);
setData(data)
})
.catch((error) => {
console.error(error);
});
}, [page, data])
const [page, setPage] = useState(0);
const [data, setData] = useState(null);
//..... other logic
return (
// .. all articles
// .. pagination onClick = setPage(// next page)
)
}
good luck!
Hook Can only be used in top level of one component.
So you can't use hook in components callback function.
You can do like below:
import React, { useRef, useEffect, useState, useCallback } from 'react';
import type { PaginationProps } from 'antd';
import { Pagination } from 'antd';
import { useGetArticlesQuery } from '../../store/api/articleApi'
const App = () => {
const { fetchData } = useGetArticlesQuery()
const onChange: PaginationProps['onChange'] = (page) => {
setCurrent(page);
fetchData(page)
};
<Pagination
current={current}
onChange={onChange}
total={total}
defaultPageSize={amount}
/>
}
Related
As you can tell from the code, I'm trying to make a custom hook that would sort the rendered cards.
When the cards first render, they show up in the default order from the API, but they get sorted only after forcing a re-render, usually by pressing Ctrl+S to save so Vite registers a change.
I'm new to custom hooks so I don't really know what I'm doing wrong, probably a problem with the dispatches inside the useEffect? How can I get the cards to sort properly?
This is the custom hook:
import { useEffect, useState } from 'react';
// Redux
import { getAllBosses, SortBosses } from '../redux/slices/Bosses';
import { useDispatch, useSelector } from 'react-redux';
export default function useFetch(value) {
const dispatch = useDispatch();
const { list: bosses } = useSelector(state => state.bosses);
const [type, setType] = useState([]);
useEffect(() => {
value === 'default'
? dispatch(getAllBosses())
: dispatch(SortBosses(value));
setType(bosses);
}, [value]);
return type;
}
And this is the component:
import { useEffect, useState } from 'react';
// Redux
// import { getAllBosses } from '../../redux/slices/Bosses';
// import { useDispatch, useSelector } from 'react-redux';
// components
import { BossCard, Loading, Pagination, Error } from '#components';
import { useFetch } from '../../hooks';
import { Cards } from '#components/styles/Cards.style';
function BossesList() {
// const dispatch = useDispatch();
const bosses = useFetch('ztoa');
const [currentPage, setCurrentPage] = useState(1);
const bossPerPage = 8;
const indexOfLastBoss = currentPage * bossPerPage;
const indexOfFirstBoss = indexOfLastBoss - bossPerPage;
const currentBosses = bosses.slice(indexOfFirstBoss, indexOfLastBoss);
const pages = pageNumber => {
setCurrentPage(pageNumber);
};
// useEffect(() => {
// dispatch(getAllBosses());
// }, [dispatch]);
useEffect(() => {
setCurrentPage(1);
}, [bosses]);
return (
<>
<Cards>
{currentBosses.length !== 0 ? (
currentBosses.map((boss, index) =>
boss.error ? (
<Error key={index} error={'No boss with that name.'} />
) : (
<BossCard
key={boss.id}
id={boss.id}
name={boss.name}
image={boss.image}
/>
)
)
) : (
<div>
<Loading />
</div>
)}
</Cards>
<Pagination
bossPerPage={bossPerPage}
bosses={bosses.length}
currentBosses={currentBosses}
pages={pages}
currentPage={currentPage}
/>
</>
);
}
export default BossesList;
I'm trying to make a page to show the details of each video.
I fetched multiple video data from the back-end and stored them as global state.
This code works if I go to the page through the link inside the app. But If I reload or open the URL directory from the browser, It can not load the single video data.
How should I do to make this work?
Thanx
Single Video Page
import { useState, useEffect, useContext } from "react";
import { useParams } from "react-router-dom";
import { VideoContext } from "../context/videoContext";
const SingleVideo = () => {
let { slug } = useParams();
const [videos, setVideos] = useContext(VideoContext);
const [video, setVideo] = useState([]);
useEffect(() => {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}, []);
return (
<>
<div>
<h1>{video.title}</h1>
<p>{video.content}</p>
<img src={video.thumbnail} alt="" />
</div>
</>
);
};
export default SingleVideo;
Context
import React, { useState, createContext, useEffect } from "react";
import Axios from "axios";
import { AxiosResponse } from "axios";
export const VideoContext = createContext();
export const VideoProvider = (props) => {
const [videos, setVideos] = useState([]);
const config = {
headers: { "Access-Control-Allow-Origin": "*" },
};
useEffect(() => {
//Fetch Vidoes
Axios.get(`http://localhost:5000/videos`, config)
.then((res: AxiosResponse) => {
setVideos(res.data);
})
.catch((err) => {
console.log(err);
});
}, []);
return (
<VideoContext.Provider value={[videos, setVideos]}>
{props.children}
</VideoContext.Provider>
);
};
I think the reason is because when you refresh the app, you fetch the video data on context and the useEffect on your single video page component runs before you receive those data.
To fix you can simply modify slightly your useEffect in your single video component to update whenever you receive those data:
useEffect(() => {
if (videos.length) {
const result = videos.find((videos) => {
return videos.uuid === slug;
});
setVideo((video) => result);
}
}, [videos]);
I defined a useCallback function in a functional component and it is used in useEffect in the same functional component. in this case, Is the function optimized?
import { useCallback, useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchMetadata } from '../features/metadata/metadataSlice';
import { getChangedItems } from '../app/evergreenAPI';
const useWatchChanges = () => {
const dispatch = useDispatch();
const { isLogged } = useSelector((state) => state.auth);
const handleChangedItems = useCallback((changedItems) => {
console.log('...doing something with', changedItems);
}, []);
const fetchChangedItems = async () => {
if (!isLogged) return false;
try {
const changedItems = await getChangedItems();
changedItems &&
setTimeout(() => {
handleChangedItems(changedItems);
});
fetchChangedItems();
} catch (e) {
console.log(e);
fetchChangedItems();
}
};
useEffect(() => {
fetchChangedItems();
}, [isLogged, fetchChangedItems]);
};
export default useWatchChanges;
You are missing dependencies in your useCallback dependency array.
const handleChangedItems = useCallback((changedItems) => {
console.log('...doing something with', changedItems);
}, [changedItems]);
Moreover it does not make any sense to use useCallback here probably, as useCallback also takes execution time and resources. But your handleChangedItems does not do anything that is worth to put into useCallback because of execution time / resources. Please have a look at:
https://kentcdodds.com/blog/usememo-and-usecallback
The useEffect below renders, fetches data, and displays it once (using an empty array for 2nd parameter in useEffect).
I need it to rerun useEffect everytime the user changes data to the database (when user uses axios.post).
What i've tried
using [tickets], but that just causes the useEffect to run infinitly
also using [tickets.length] and [tickets, setTickets]
trying to use props as parameter but didnt find anything useful
import React, { useState, createContext, useEffect } from "react";
import axios from "axios";
export const TicketContext = createContext();
export const TicketProvider = (props) => {
console.log(props);
const [tickets, setTickets] = useState([]);
useEffect(() => {
getTickets();
console.log("1", { tickets });
}, []);
const getTickets = async () => {
const response = await axios.get("http://localhost:4000/tickets/");
setTickets(response.data);
};
return <TicketContext.Provider value={[tickets, setTickets]}>{props.children}
</TicketContext.Provider>;
};
import React from "react";
import { useState, useEffect, useContext } from "react";
import Ticket from "../Ticket";
import { TicketContext } from "../contexts/TicketContext";
import AddBacklog from "../addData/AddBacklog";
const TicketDisplay = (props) => {
const [tickets, setTickets] = useContext(TicketContext);
return (
<div className="display">
<p>Antony Blyakher</p>
<p>Number of Tickets: {tickets.length}</p>
<div className="backlog">
<h1>Backlog</h1>
{tickets.map((currentTicket, i) => (
<div className="ticketBlock">
<Ticket ticket={currentTicket} key={i} />
</div>
))}
</div>
</div>
);
const AddBacklog = (props) => {
const [tickets, setTickets] = useState("");
...
axios.post("http://localhost:4000/tickets/add", newTicket).then((res) => console.log(res.data));
setTickets((currentTickets) => [...currentTickets, { name: name, status: "backlog", id: uuid() }]);
};
You'll need to watch for tickets and return if it has data to not cause infinite loop:
useEffect(() => {
if (tickets.length) return // so, we call just once
getTickets();
console.log("1", { tickets });
}, [tickets]);
const fetchData = () => {
axios.get("http://localhost:7000/api/getData/").then((response) => {
console.log(response.data);
if (response.data.success) {
SetIsLoading(false);
}
setDataSource(response.data.data);
});
};
useEffect(() => {
fetchData();
if (fetchData.length) fetchData();
}, [fetchData]);
by this you can fetch the data in real-time as any change in data occurs.
I have the following simple use case:
The component ServerRendered is used to render markup that is retrieved from a server using the property url . In ServerRendered I use useEffect to load the markup from the back-end, set the state to the current markup and render it in a div.
The property init optionally specifies a function that should be executed after having rendered the markup.
How would I run the init function after the markup has been rendered?
/* eslint-disable react/no-danger */
import React, {useState, useEffect} from 'react';
import axios from 'axios';
type ServerRenderedPropsType = {
url: string,
init?: () => void,
};
function ServerRendered(props: ServerRenderedPropsType) {
const [html, setHtml] = useState('');
useEffect(() => {
async function fetchData() {
const result = await axios(props.url);
setHtml(result.data.title);
}
fetchData();
}, [props.url]);
return <div dangerouslySetInnerHTML={{__html: html}} className="serverRendered" />;
}
export default ServerRendered;
You have just to do another effet when you receive your data
import React, {useState, useEffect, useCallback} from 'react';
import axios from 'axios';
type ServerRenderedPropsType = {
url: string,
init?: () => void,
};
function ServerRendered(props: ServerRenderedPropsType) {
const [html, setHtml] = useState('');
const fetchData = useCallback(async () => {
const result = await axios(props.url);
setHtml(result.data.title);
}, [props.url])
useEffect(() => {
fetchData();
}, [fetchData]);
useEffect(() => {
// do what you want here
if (!html) return;
props.init()
}, [html])
return <div dangerouslySetInnerHTML={{__html: html}} className="serverRendered" />;
}
export default ServerRendered;