The main page brings data.
When page url changes, useInfiniteQuery can't get JSON data.
What I suspect is this
On the network in the browser console it shows:'You need to enable JavaScript to run this app.'
this is my code
File Structure
fublic/
data/
youtube.json
...
src/
conponent/
nav/
nav.jsx
hooks/
useList.jsx
routes/
mainContent.jsx
router configuration
const router = createBrowserRouter([
{ path: '/', element: <Nav />,
children: [
{ index: true, element: <MainContent />},
{ path: 'videos/:searchId', element: <MainContent />},
{ path: '/videos/detail/:videoId', element: <VideoDetail />}
]
}
])
Nav.jsx code
export default function Nav() {
const [list, setList] = useState(
{ listUrl: 'data/youtube&pageToken=', key: 'mainList'})
const handleUrl = (url, key) => {
setList({listUrl: url, key})}
return (
<>
<header className='main_heder'>
<h1>logo</h1>
<NavSearch
handleUrl={handleUrl}
/>
</header>
<Outlet context={list} />
</>
);
}
MainContent.jsx code
export default function MainContent() {
const list = useOutletContext()
const [data, status, hasNextPage, fetchNextPage, nowtime] = useList(list)
const { ref, inView } = useInView({threshold: 0});
const onFetchNextPage = () => {
if (hasNextPage && inView) {
fetchNextPage();}}
const getFilteredLists = (list) => {
return list.pages.map((group) => group.items.filter((item) => !item.id.channelId)
)}
useEffect(onFetchNextPage,[inView])
return (
<>
<InView
as='div'
onChange={onFetchNextPage}
>
<main className='main_contents'>
{status === 'loading' ? <p>Loading...</p> :
getFilteredLists(data).map((group) => (
group.map((movie) => (
<MainMovieList
key={movie.id}
movie={movie}
time={nowtime}
/>
))))}
<div ref={ref}></div>
<>
</>
</main>
</InView>
</>
);
}
useList.jsx code
export default function useList( {listUrl}) {
const fetchProducts = async ({ pageParam = '' }) => {
const data = await (
await fetch(`${listUrl + pageParam}.json`)
).json();
return data;
};
const {
data,
error,
fetchNextPage,
hasNextPage,
status,
} = useInfiniteQuery([listUrl,state], fetchProducts, {
getNextPageParam: (lastPage) => lastPage.nextPageToken,
refetchOnWindowFocus: false,
enabled:true
})
const nowtime = new Date().getTime()
return [data, status, hasNextPage, fetchNextPage, nowtime]
}
I want to receive json file when 'listUrl' value is changed in useList.jsx.
Related
I have a react component that fetches from API with createAsyncThunk and cannot understand a behaviour that happens on Mount in this part:
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
if(feedResponse) return(
<div className={styles.feed}>
{feedResponse.map(({id}) => {
return(
<Link to={`/thread=${id}`} key={id}>
<Thread key={id} id={id}/>
</Link>
)
})}
</div>
)
If I remove if(feedResponse) next to the return(...) the component will crash because it will try to render before feedResponse status has data. Why isn't that covered by the first two IFs?
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
It is my understanding that there is no scenario where we have an inLoading = false & feedResponse = null
Here is the complete code if needed:
Feed.js
export const Feed = () => {
const dispatch = useDispatch()
const feedResponse = useSelector(selectFeedResponse)
const isLoading = useSelector(isLoadingFeed)
const failedToLoad = useSelector(failedToLoadFeed)
const location = useLocation()
useUpdateEffect(() => {
dispatch(SearchThunk(searchTerm))
}, searchTerm)
useUpdateEffect(() => {
dispatch(homeThunk(location.pathname+'.json'))
}, location)
if(isLoading===true) return <div>Loading...</div>
if(failedToLoad===true) return <div>Error loading feed</div>
if(feedResponse) return(
<div className={styles.feed}>
{feedResponse.map(({id}) => {
return(
<Link to={`/thread=${id}`} key={id}>
<Thread key={id} id={id}/>
</Link>
)
})}
</div>
)
}
feedSlice.js
export const homeThunk = createAsyncThunk(
'feed/homeThunk',
async (homePath) => {
const response = await fetch(`https://www.reddit.com${homePath}`)
const json = await response.json()
const threads = json.data.children.map(thread => {
return {
id: thread.data.id,
subreddit: thread.data.subreddit,
title: thread.data.title,
author: thread.data.author,
thumbnail: thread.data.thumbnail,
created: thread.data.created,
score: thread.data.score,
num_comments: thread.data.num_comments
}
})
return threads
}
)
export const feedSlice = createSlice({
name: 'feed',
initialState: {
feedResponse: '',
isLoadingFeed: false,
failedToLoadFeed: false
},
extraReducers: (builder) => {
builder
.addCase(homeThunk.pending, (state) => {
state.isLoadingFeed = true
state.failedToLoadFeed = false
})
.addCase(homeThunk.fulfilled, (state, action) => {
state.isLoadingFeed = false
state.failedToLoadFeed = false
state.feedResponse = action.payload
})
.addCase(homeThunk.rejected, (state) => {
state.isLoadingFeed = false
state.failedToLoadFeed = true
})
}
})
export const selectFeedResponse = state => state.feed.feedResponse
export const isLoadingFeed = state => state.feed.isLoadingFeed
export const failedToLoadFeed = state => state.feed.failedToLoadFeed
export default feedSlice.reducer
feedUtilities.js
import { useEffect, useRef } from "react";
export const useUpdateEffect = (effect, deps = []) => {
const isFirstMount = useRef(true);
useEffect(() => {
if(!isFirstMount.current) effect()
else isFirstMount.current = false
}, [deps]);
}
So basically every time a navigate to a different page in this case from home page to /blog using next-link I get Unhandled Runtime Error
TypeError: destroy is not a function
but when I refresh the page the page loads correctly or if I type in the URL manually in the browser for instance localhost:3000/blog
here is a screenshots of the error
and here is the code for the blog page
///blog page
export default function Blog(props) {
const { posts, tags } = props;
function switchMode() {
props.setMode(props.mode === 'light' ? 'dark' : 'light');
}
return (
<>
<Head>
<title>Blog</title>
<link rel="icon" href="/favicon.ico" />
<meta name="description" content="Yushae Raza's personal blog" />
</Head>
<main style={{ paddingTop: "5em" }}>
<Container>
<Typography variant="h1" align="center" >
Blog
</Typography>
<Divider />
<br></br>
<Grid container spacing={2}>
<Grid item xs={12} md={8}>
{posts.map((post, idx) => (<>
<Post
title={post.title}
publishedAt={post.published_at}
author={post.authors[0].name}
imageUrl={post.feature_image}
description={post.excerpt}
category={post.tags[0].name}
slug={post.slug} /><br></br>
</>)
)}
</Grid>
<Grid item xs={12} md={4}>
<Sidebar tags={tags} />
</Grid>
</Grid>
</Container>
</main>
</>
)
}
export async function getStaticProps() {
// get posts from our api
const { posts } = await api.getPosts(0);
const { tags } = await api.getTags();
// console.log(posts)
return {
props: { posts, tags },
revalidate: 10
};
}
//navbar
const pages = [
{ name: 'Blog', link: '/blog' },
]
{pages.map((page, idx) => (
<MenuItem key={idx} onClick={handleCloseNavMenu}
sx={{ flexGrow: 1 }}>
<Link href={page.link}>
{page.name}
</Link>
</MenuItem>
))}
//api.js
const backendUrl = 'https://yr-blog.herokuapp.com/'
export const getPosts = (page) => {
console.log(process.env.API_KEY)
return fetch(
`${backendUrl}ghost/api/v4/content/posts/?key=${process.env.API_KEY}&order=published_at%20desc&include=authors,tags&page=${page}&fields=title,slug,feature_image,published_at,excerpt&formats=plaintext`
).then((res) => res.json())
}
export const getSlugs = (page) => {
console.log(process.env.API_KEY)
return fetch(
`${backendUrl}ghost/api/v4/content/posts/?key=${process.env.API_KEY}&order=published_at%20desc&fields=slug`
).then((res) => res.json())
}
export const getTags = () => {
return fetch(
`${backendUrl}ghost/api/v4/content/tags/?key=${process.env.API_KEY}&fields=name,slug`
).then((res) => res.json())
}
export const getTagsSlugs = () => {
return fetch(
`${backendUrl}ghost/api/v4/content/tags/?key=${process.env.API_KEY}&fields=slug`
).then((res) => res.json())
}
export const getTagName = (slug) => {
return fetch(`${backendUrl}ghost/api/v4/content/tags/?key=${process.env.API_KEY}&filter=slug:${slug}&fields=name`).then((res) => res.json());
}
export const getPost = (slug) => {
return fetch(`${backendUrl}ghost/api/v4/content/posts/?key=${process.env.API_KEY}&filter=slug:${slug}&order=published_at%20desc&include=authors,tags&fields=html,title,slug,feature_image,published_at`).then((res) => res.json())
}
export const getPostsByTag = (tag, page) => {
return fetch(`${backendUrl}ghost/api/v4/content/posts/?key=${process.env.API_KEY}&filter=tags:${tag}&order=published_at%20desc&include=authors,tags&page=${page}&fields=title,slug,feature_image,published_at,excerpt&formats=plaintext`).then((res) => res.json())
}
//home page
export default function Home(props) {
useEffect(() => {
TagCloud(".test", [
'JavaScript', 'CSS', 'HTML',
'C', 'C++', 'React',
'Python', 'Java', 'git',
'django', 'Node.js', 'Express.js',
'MySQL', 'jQuery',
'Bootstrap', 'Material-UI',
'Next.js', 'React-Native',
'Firebase', 'Apache',
'MongoDB', 'SQL',
'TypeScript', 'Git',
], {
radius: screen.width > 500 ? 300 : 100,
// slow, normal, fast
maxSpeed: 'fast',
initSpeed: 'fast',
// 0 = top
// 90 = left
// 135 = right-bottom
direction: 135,
keep: false,
});
return () => {
let tagClouds = document.getElementsByClassName('tagcloud');
for (let i = 0; i < tagClouds.length; i++) {
tagClouds[0].remove();
}
};
}, []);
Update 1 I narrowed down the issue to the animateletters component which detailed below
const AnimatedLetters = (props) => {
const [letterClass, setLetterClass] = useState('letter-intro');
useEffect(() => {
return setTimeout(() => {
setLetterClass('letter-hover');
}, 4000)
}, []);
const letters = props.title.split("");
const length = letters.length;
return (
<StyledSpan length={length} useDefaultColor={props.useDefaultColor}>
{letters ? letters.map((char, index) => {
return (
<span className={`letter ${letterClass} _${index}`} key={index}>{char}</span>
);
}) : null}
</StyledSpan>
)
}
export default AnimatedLetters;
Use case is to to open
http://localhost:3000/users/101
This is how I would like my page layout to be and Element pull in the second code snippet in [id].tsx
import CTASection from '../../components/samples/CTASection';
import { User } from "../../interfaces";
import { GetStaticProps, GetStaticPaths } from "next";
import Element from "pages/users/element";
const Element = ({ item, errors }: Props) => {
return (
<Box>
<Element />
<CTASection />
</Box>
)
}
export default Id;
export const getStaticPaths: GetStaticPaths = async () => {
// Get the paths we want to pre-render based on users
const paths = sampleUserData.map((user) => ({
params: { id: user.id.toString() },
}));
return { paths, fallback: false };
};
export const getStaticProps: GetStaticProps = async ({ params }) => {
try {
const id = params?.id;
const item = sampleUserData.find((data) => data.id === Number(id));
return { props: { item } };
} catch (error) {
return { props: { errors: 'ERROR Loading Data' } };
}
};
However it only renders the query parameters if I insert my element.tsx page in a non-modular fashion like this:
...
return (
<Box>
<Grid gap={2}>
<Heading as="h2" fontSize={{ base: "lg", sm: "3xl" }}>
Verifikation
</Heading>
<Box
backgroundColor={colorMode === "light" ? "gray.200" : "gray.500"}
padding={4}
borderRadius={4}
>
<Box fontSize={textSize} title={`${
item ? item.name : "User Detail"
}`}>
{item && <ListDetail item={item} />}
</Box>
</Box>
</Grid>
<CTASection />
</Box>
)
...
This is the ListDetail.tsx:
import * as React from "react";
import { User } from "../../interfaces";
type ListDetailProps = {
item: User;
};
const ListDetail = ({ item: user }: ListDetailProps) => (
<div>
<h1>User: {user.name}</h1>
<p>ID: {user.id}</p>
</div>
);
export default ListDetail;
you can use gerServerSideProps
export const getServerSideProps = async ({ req, res, query, params }) => {
// your code...
// you have access to req, res, query, params, headers, cookies, and many more.
return {
props: {}
}
}
I have the following code that passing leadsBuilder props to lead in the LeadBuilderSingle componenet. It has an array in a object and I access that array and try to map over it but it returns undefined. The data is being waited on and I am using isLoading, so I am not sure what is causing this error. It loads on first loading, but on page refresh gives me undefined.
import React, { useState, useEffect } from "react";
import Dasboard from "./Dashboard";
import { Container } from "../styles/Main";
import { LeadsBuilderCollection } from "../../api/LeadsCollection";
import { LeadBuilderSingle } from "../leads/LeadBuilderSingle";
import { useTracker } from "meteor/react-meteor-data";
const LeadCategoriesAdd = ({ params }) => {
const { leadsBuilder, isLoading } = useTracker(() => {
const noDataAvailable = { leadsBuilder: [] };
if (!Meteor.user()) {
return noDataAvailable;
}
const handler = Meteor.subscribe("leadsBuilder");
if (!handler.ready()) {
return { ...noDataAvailable, isLoading: true };
}
const leadsBuilder = LeadsBuilderCollection.findOne({ _id: params._id });
return { leadsBuilder };
});
return (
<Container>
<Dasboard />
<main className="">
{isLoading ? (
<div className="loading">loading...</div>
) : (
<>
<LeadBuilderSingle key={params._id} lead={leadsBuilder} />
</>
)}
</main>
</Container>
);
};
export default LeadCategoriesAdd;
import React from "react";
export const LeadBuilderSingle = ({ lead, onDeleteClick }) => {
console.log(lead);
return (
<>
<li>{lead.type}</li>
{lead.inputs.map((input, i) => {
return <p key={i}>{input.inputType}</p>;
})}
</>
);
};
FlowRouter.route("/leadCategories/:_id", {
name: "leadeBuilder",
action(params) {
mount(App, {
content: <LeadCategoriesAdd params={params} />,
});
},
});
try this :
lead.inputs && lead.inputs.map ((input, i) => {...}
I am pushing to an array
options.push(
{
value: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />,
label: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
})
But always When I need this array it refreshes and I get at the start - '' and then actual data, I need to get once to interact with its.
export default function DataFteching({ x, y }) {
const [adrress, setAdrress] = useState(null)
const [loading, setLoading] = useState(true)
const region = /place/
useEffect(() => {
async function FtechData() {
const token = 'pk.eyJ1IjoiYW5kcmlpbXNuIiwiYSI6ImNrZGYzZ200YTJudXQyeHNjMjk2OTk2bjUifQ.njqMX6x6U946yjJdWwA7mA';
await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${x},${y}.json?access_token=${token}`)
.then(res => {
// console.log(res.data.features.find(place => place.id.match(region)).text)
setAdrress(res.data)
})
.catch(err => console.log(err))
.finally(() => setLoading(false));
}
FtechData();
}, [])
if (loading) return false;
// console.log({ adrress.features.find(place => place.id.match(region)).text })
console.log(`${(adrress.features.find(place => place.id.match(region)).text)}`)
return `${(adrress.features.find(place => place.id.match(region)).text)}`
}
The Code Sandbox have dependencies error but the solution could be something like this
import React, { useEffect, useState } from "react";
import { Link, graphql, useStaticQuery } from "gatsby";
import Layout from "../components/layout";
import blogStyles from "./Styles/blog.module.scss";
import Head from "../components/head";
import Adrress from "../components/Map/ftechAdrress";
import WithCallbacks from "../components/Search-panel";
const BlogPage = () => {
const [options, setOptions] = useState([]);
const data = useStaticQuery(graphql`
query {
allContentfulBlogPost(
sort: { fields: publishedDate, order: DESC }
filter: { node_locale: { eq: "en-US" } }
) {
edges {
node {
title
slug
publishedDate(formatString: "MMM Do, YYYY")
image {
file {
url
}
}
location {
lat
lon
}
breed
find
}
}
}
}
`);
useEffect(() => {
const tmp = [];
data.allContentfulBlogPost.edges.forEach((edge) => {
tmp.push({
value: (
<Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
),
label: <Adrress x={edge.node.location.lon} y={edge.node.location.lat} />
});
});
setOptions([...tmp]);
}, [data]);
return (
<Layout>
<Head title="Blog" />
<h1>lost pets</h1>
{console.log(options)}
<WithCallbacks options={options} />
<ol className={blogStyles.posts}>
{options.map((edge) => {
const styles = edge.node.image
? `url("${edge.node.image.file.url}")`
: `url("https://cdn.pixabay.com/photo/2019/07/30/05/53/dog-4372036__340.jpg")`;
return (
<li>
<Link to={`/blog/${edge.node.slug}`}>
<div
style={{ backgroundColor: "pink", backgroundImage: styles }}
>
<h2>{edge.node.title}</h2>
<p>{edge.node.publishedDate}</p>
<p>Порода: {edge.node.breed}</p>
<p>
Статус: <span>{edge.node.find ? "Найден" : "Потерян"}</span>
</p>
<p>
city:{" "}
<Adrress
x={edge.node.location.lon}
y={edge.node.location.lat}
/>
</p>
</div>
</Link>
</li>
);
})}
</ol>
</Layout>
);
};
export default BlogPage;
const options = []
const region = /place/
data.allContentfulBlogPost.edges.forEach(async (edge) => {
await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${edge.node.location.lon},${edge.node.location.lat}.json?access_token=${token}`)
.then(response => response.json())
.then(json => options.push({
value: json.features.find(place => place.id.match(region)).text,
label: json.features.find(place => place.id.match(region)).text
}))
console.log(options)
}
)