Nextjs - Amplify - Duplicate content on [slug].js - javascript

I'm pretty sure I'm overseeing something here. This [slug].js duplicates the content on a single page. If I click on the Frog link, it'll show me the Frog link and the other data. The slug links work, but they also show other content from other links. I'm trying to make it to only the link's content to display.
import { DataStore, withSSRContext } from "aws-amplify";
import { Post } from "../../models";
import Markdown from "react-markdown";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import Head from "next/head";
export default function PostComponent() {
const [posts, setPosts] = useState([]);
useEffect(() => {
async function fetchPosts() {
const postData = await DataStore.query(Post);
setPosts(postData);
}
const subscription = DataStore.observe(Post).subscribe(() => fetchPosts());
fetchPosts();
return () => subscription.unsubscribe();
}, []);
const router = useRouter();
if (router.isFallback) {
return (
<div className="container text-center">
<h1>I apologize, the page is still loading.</h1>
</div>
);
}
return posts.map((post) => {
return (
<>
<Head key={post.title}>
<title>{post.seoTitle}</title>
<meta
name="viewport"
content="initial-scale=1.0, width=device-width"
/>
<meta name="description" content={post.seoDescription} />
</Head>
<div className="container">
<h1>{post.title}</h1>
<Markdown>{post.content}</Markdown>
</div>
</>
);
});
}
export async function getStaticPaths(req) {
const { DataStore } = withSSRContext(req);
const posts = await DataStore.query(Post);
const paths = posts.map((post) => ({ params: { slug: post.id } }));
return {
paths,
fallback: true,
};
}
export async function getStaticProps(req) {
const { DataStore } = withSSRContext(req);
const { params } = req;
const { id } = params;
const post = await DataStore.query(Post, id);
return {
props: {
post: JSON.parse(JSON.stringify(post)),
},
revalidate: 1,
};
}
Thank you help in advance. You can see the live site at https://main.d23urctcf9nr2q.amplifyapp.com/

Related

invalidateQueries doesn't refetch/refresh the page

I have a button that, on click, has to perform a thing that makes a list displayed on my page to change, and in theory the page should reload. However, that never happens, no matter how many times I click my button.
Full code of my button:
import React from 'react';
import { useSaveOrderItemsForList } from '../../hooks/Lists/useSaveOrderItemsForList';
import ErrorIndicator from '../shared/ErrorIndicator';
import LoadingButton from '../shared/LoadingButton';
import { valueState as valueStateAtom } from '../../atoms/orderItemsAtom';
import { useSetRecoilState } from 'recoil';
export default function SaveOrderItemsButton({ orderItems, listID }) {
const { isError, error, isLoading, mutate } = useSaveOrderItemsForList(orderItems, listID);
const setValue = useSetRecoilState(valueStateAtom);
const handleClick = () => {
mutate(orderItems, listID);
setValue([]);
}
return (
<div className={'w-100'}>
<br />
<ErrorIndicator isError={isError} error={error} />
<LoadingButton
className={'w-100'}
variant={'success'}
loading={isLoading}
onClick={handleClick}
>
Save
</LoadingButton>
</div>
);
}
As for the code of my custom hook:
import { getToken } from '../../tokens/getToken';
import { basePath } from '../../config/basePath';
import { getTokenAuthHeaders } from '../../functions/sharedHeaders';
import { useMutation, useQueryClient } from 'react-query';
async function saveOrderItemsForList(orderItems, listID) {
const token = await getToken();
const response = await fetch(`${basePath}/lists/save_order_items/${listID}`, {
method: 'PUT',
body: JSON.stringify({ orderItems }),
headers: getTokenAuthHeaders(token)
});
return response.json();
}
export function useSaveOrderItemsForList() {
const queryClient = useQueryClient();
return useMutation(saveOrderItemsForList,
{
onSuccess: () => {
return queryClient.invalidateQueries('lists');
}
}
);
}
My theory is that, since I'm managing the organizing of my list client-side, the page doesn't get updated with the information I passed (this is the code of the page that shows the list):
import Col from 'react-bootstrap/Col';
import CardsList from './CardsList';
import { useList } from '../../hooks/Cards/useList';
import useOrderItemsForCardsInList from '../../hooks/Lists/useOrderItemsForCardsInList';
import usePaginateCardsInList from '../../hooks/Cards/usePaginateCardsInList';
import LoadingAndErrorCentered from '../shared/LoadingAndErrorCentered';
export default function List({ listID }) {
const { isLoading, isError, error, data } = useList(listID);
const { data: orderItems } = useOrderItemsForCardsInList(listID);
const pagesArray = usePaginateCardsInList(orderItems, data);
return (
<Col xs={12}>
<br />
<LoadingAndErrorCentered isLoading={isLoading} isError={isError} error={error} />
{data && <CardsList cards={pagesArray} listID={listID} />}
</Col>
);
}
What do you guys think?
Edit: This is the code of my useList hook.
import { useQuery } from 'react-query';
import { getTokenAuthHeaders } from '../../functions/sharedHeaders';
import { basePath } from '../../config/basePath';
import { getToken } from '../../tokens/getToken';
async function getList(listID) {
const token = await getToken();
const response = await fetch(`${basePath}/cards/list/${listID}`, {
method: 'GET',
headers: getTokenAuthHeaders(token)
});
return response.json();
}
export function useList(listID) {
return useQuery(['cards', 'list', listID], () => {
return getList(listID);
});
}
and on my server, I have this function declared on my endpoint:
static async getList(id) {
const query = await List.findById(id).exec();
return query;
}
queryClient.invalidateQueries('lists');
vs
useQuery(['cards', 'list', listID], () => {});
You are not invalidating the right query keys, so naturally the query doesn't refetch. You need to use the correct key for invalidation, in your case:
queryClient.invalidateQueries(['cards', 'list']);

Problem when maping array - React with Redux Toolkit

I have the following problem: I use a fatch API to get all the products I have registered in my database (mongodb), then I store the result in a slice called products-slice which has an array as its initial state empty. Until then everything is in order. As I need information the time the homepage is loaded, I use the useEffect hook to fetch the products I have registered. Then I pass this array as props to a component, and make a map. The problem is that when the component loads, the information is not local.
código do backend
module.exports.fetchProduct = async (req, res) => {
try {
const products = await Product.find({});
if (products) {
res.json(products);
}
} catch (error) {
console.log(error);
}
};
productsActions.js
export const fetchProducts = () => {
return async (dispatch) => {
try {
const response = await fetch("http://localhost:xxxx/xxxxxx");
const data = await response.json();
let loadedProducts = [];
for (const key in data) {
loadedProducts.push({
id: data[key]._id,
productName: data[key].productName,
price: data[key].price,
imageUrl: data[key].imageUrl,
});
}
dispatch(setProducts(loadedProducts));
} catch (error) {
console.log(error);
}
};
};
home.jsx
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Container } from "react-bootstrap";
import {fetchProducts} from '../../store/actions/productsActions';
import Hero from "../hero/Hero";
import Footer from "../footer/Footer";
import DisplayProductsList from "../displayProduct/DisplayProductsList";
export default function Home() {
const productsInfo = useSelector((state) => state.products.products);
console.log(productsInfo);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
return (
<>
<Hero />
<DisplayProductsList products={productsInfo} />
<Container fluid>
<Footer></Footer>
</Container>
</>
);
}
product-slice.js
const initialState = {
products: [],
};
const productSlice = createSlice({
name: "product",
initialState,
reducers: {
setProducts(state, action) {
state.products.push(action.payload);
},
},
});
export const { setProducts } = productSlice.actions;
export default productSlice.reducer;
component where I'm mapping
export default function DisplayProductsList(props) {
console.log(props);
return (
props.products.map((product) => (
<DisplayProducts
key={product.id}
imageUrl={product.imageUrl}
name={product.productName}
price={product.price}
/>
))
);
}
console.log output in the above component
enter image description here

Getting undefined props in functional react components

How to pass the {requests} prop to the RequestRow component after executing the setRequests? My understanding is that the requests get initialized as undefined in the beginning and before being set with the asynchronously called object, it gets passed to the RequestRow component as undefined, and the error occurs.
import React, { useState, useEffect } from 'react';
import 'semantic-ui-css/semantic.min.css';
import Layout from '../../../components/Layout';
import { Button } from 'semantic-ui-react';
import { Link } from '../../../routes';
import Campaign from '../../../blockchain/campaign';
import { Table } from 'semantic-ui-react';
import RequestRow from '../../../components/RequestRow';
const RequestsIndex = ({ address }) => {
const { Header, Row, HeaderCell, Body } = Table;
const campaign = Campaign(address);
const [requestCount, setRequestCount] = useState();
const [requests, setRequests] = useState([]);
const getRequests = async () => {
const count = await campaign.methods.getRequestsCount().call();
setRequestCount(count);
};
let r;
const req = async () => {
r = await Promise.all(
Array(parseInt(requestCount))
.fill()
.map((_element, index) => {
return campaign.methods.requests(index).call();
})
);
setRequests(r);
};
useEffect(() => {
getRequests();
if (requestCount) {
req();
}
}, [requestCount]);
return (
<Layout>
<h3>Requests List.</h3>
<Link route={`/campaigns/${address}/requests/new`}>
<a>
<Button primary>Add Request</Button>
</a>
</Link>
<Table>
<Header>
<Row>
<HeaderCell>ID</HeaderCell>
<HeaderCell>Description</HeaderCell>
<HeaderCell>Amount</HeaderCell>
<HeaderCell>Recipient</HeaderCell>
<HeaderCell>Approval Count</HeaderCell>
<HeaderCell>Approve</HeaderCell>
<HeaderCell>Finalize</HeaderCell>
</Row>
</Header>
<Body>
<Row>
<RequestRow requests={requests}></RequestRow>
</Row>
</Body>
</Table>
</Layout>
);
};
export async function getServerSideProps(context) {
const address = context.query.address;
return {
props: { address },
};
}
export default RequestsIndex;
The RequestRow component is shown below. It takes in the {requests} props, which unfortunately is undefined.
const RequestRow = ({ requests }) => {
return requests.map((request, index) => {
return (
<>
<div>Request!!!</div>
</>
);
});
};
export default RequestRow;
The snapshot of the error is shown below:
I think React is trying to render your component before your promises resolve. If that's the case, all you need to do is set a default value (an empty array in your case) for your requests.
const [requests, setRequests] = useState([]);
May the force be with you.

Cannot display Fetched data to the UI in React

it doesn't show an error and the project works just fine. I can log the data to the console as well. but it doesn't display in the UI. this is a tutorial project on youtube
I'm getting data from the API and passing that to the tours and tour components. and Tour component displays the fetched data.
App component
import React, { useState, useEffect } from "react";
import Loading from "./Loading";
import Tours from "./Tours";
// ATTENTION!!!!!!!!!!
// I SWITCHED TO PERMANENT DOMAIN
const url = "https://course-api.com/react-tours-project";
function App() {
const [loading, setLoading] = useState(true);
const [tours, setTours] = useState([]);
const fetchTours = async () => {
try {
const response = await fetch(url);
const tours = await response.json();
setLoading(false);
setTours(tours);
} catch (error) {
setLoading(true);
console.log(error);
}
};
useEffect(() => {
fetchTours();
}, []);
if (loading) {
return (
<main>
<Loading />
</main>
);
}
return (
<main>
<Tours tours={tours} />
</main>
);
}
export default App;
Tours component
import React from "react";
import Tour from "./Tour";
const Tours = ({ tours }) => {
return (
<section>
<div className="title">
<h2>Our Tours</h2>
<div className="underline"></div>
</div>
<div>
{tours.map((tour, index) => {
return <Tour key={tour.id} {...tours} />;
})}
</div>
</section>
);
};
export default Tours;
Tour Component
import React, { useState } from "react";
const Tour = ({ id, image, info, price, name }) => {
return (
<article className="single-tour">
<img src={image} alt={name} />
<footer>
<div className="tour-info">
<h4>{name}</h4>
<h4 className="tour-price">AUD{price}</h4>
</div>
<p>{info}</p>
<button className="delete-btn">Not Interested</button>
</footer>
</article>
);
};
export default Tour;
Try this code:
useEffect(async () => {
await fetchTours();
}, []);
I think your UI has not updated after the data arrived. You need to wait for your data is fetched.
Try to remove the setting of state in the function and move it to use effect. Have the API call only return the list instead of having it retrieving the list and setting the state.
const fetchTours = async () => {
const response = await fetch(url);
const tours = await response.json();
return tours;
};
useEffect(() => {
const fetchAndSetTourState = async () => {
const data = await fetchTours();
setTours(data);
setLoading(false);
}
fetchAndSetTourState();
}}, []);

Fetching translations based on language header with axios isn't working

I'm trying to build website consist of one page containing many components.
I want to fetch data for each component
so I try use Promise.all() in index page
but the problem is that translations from API don't appear only one language appears.
I used Next.js Internationalized Routing,
but when I only make axios.get() for one component it works.
What is the problem & how can I solve that?
header.js
import Link from 'next/link';
import { useRouter } from 'next/router';
import en from './locales/en';
import ar from './locales/ar';
import Axios from 'axios';
import Cookie from 'js-cookie';
import {useState } from 'react';
const Header = () => {
const router = useRouter();
const [langCode, setLangCode] = useState('en');
Axios.defaults.headers.common['Language'] = langCode;
const { locale } = router;
const t = locale === 'en' ? en : ar;
const changeLanguage = (locale) => {
Cookie.set('lang', locale);
router.push(router.pathname, router.asPath, { locale });
setLangCode(locale);
};
const lang = Cookie.get('lang')
return (
<header>
<button onClick={() => changeLanguage(lang == 'en' ? 'ar' : 'en')}>
change lang
</button>
<ul>
<li>
<Link href="/">
<a>{t.home}</a>
</Link>
</li>
</ul>
</header>
);
};
export default Header;
index.js
import Axios from "axios";
import Header from "../components/Header";
const index = ({ data }) => {
return (
<div>
<Header />
<div dangerouslySetInnerHTML={{ __html: data.details}}/>
</div>
);
};
index.getInitialProps = async () => {
const res = await Axios.get(`https://api.trueapps.co/api/who-we-are`);
const data = await res.data.data;
return { data };
};
export default index;
AND this is the code in index.js when I use Promise.all().
index.js
import Axios from "axios";
import Header from "../components/Header";
const index = (data) => {
console.log(data.about);
console.log(data.services);
console.log(data.team);
return (
<div>
<Header />
</div>
);
};
index.getInitialProps = async () => {
const [about, team, services] = await Promise.all([
fetch(`https://api.trueapps.co/api/who-we-are`).then((r) => r.json()),
fetch(`https://api.trueapps.co/api/team`).then((r) => r.json()),
fetch(`https://api.trueapps.co/api/services`).then((r) => r.json()),
]);
return { about, team, services};
};
export default index;
The issue is that you're setting the default Language header in axios (Axios.defaults.headers.common['Language'] = langCode;) but then making the requests using fetch.
Using axios to make the requests in index.getInitialProps should do the trick.
index.getInitialProps = async () => {
const [aboutRes, teamRes, servicesRes] = await Promise.all([
Axios.get(`https://api.trueapps.co/api/who-we-are`),
Axios.get(`https://api.trueapps.co/api/team`),
Axios.get(`https://api.trueapps.co/api/services`)
]);
return {
about: aboutRes.data,
team: teamRes.data,
services: servicesRes.data
};
};
Looks like you are mixing Axios and fetch API together. Maybe you need to change to Axios's get instead of fetch API from the below code.
index.getInitialProps = async () => {
const [about, team, services] = await Axios.all([
await fetch(`https://api.trueapps.co/api/who-we-are`).then((r) => r.json()),
fetch(`https://api.trueapps.co/api/team`).then((r) => r.json()),
fetch(`https://api.trueapps.co/api/services`).then((r) => r.json()),
]);
return { about, team, services};
};

Categories

Resources