Card element with favoris cannot trigger API call - next.js

I am really lost with Nextjs. I seem to have understood that I need to use swr but I am not sure that it will work in my case. or even how to make it work.
I have products which have the same component in several pages in my site. On this component, I have a favorites button which must be triggered on all these pages. on click, it must make a POST or DELETE, with a token and the product id.
My component looks like this for the moment and of course, postFavorites and deleteFavoris don't work:
import React, { useEffect, useState } from "react"
import Link from 'next/link'
import Styles from "./card.module.scss"
import axios from 'axios'
export default function Card(props) {
const [favoris, setFavoris] = useState(false);
const deleteFavoris = async () => {
console.log("click");
try {
const res = await axios({
method: 'POST',
url: `${process.env.NEXT_PUBLIC_BASE_API}/favorites`,
headers: {
Authorization: "Bearer " + session.token,
},
body: {
advert: props["#id"]
}
});
} catch (error) {
if (error.response) {
throw new Error(await error.response.data.message)
}
}
}
const postFavoris = async () => {
console.log("click");
try {
const res = await axios({
method: 'DELETE',
url: `${process.env.NEXT_PUBLIC_BASE_API}/favorites`,
headers: {
Authorization: "Bearer " + session.token,
},
body: {
advert: props["#id"]
}
});
} catch (error) {
if (error.response) {
throw new Error(await error.response.data.message)
}
}
}
const handleClick = () => {
if (favoris === true) {
deleteFavoris()
} else {
postFavoris()
}
setFavoris(!favoris)
}
return (
<div className={Styles.card}>
<Link href={"/annonce?id="+props.id} passHref>
<a className={Styles.imageCard} style={{backgroundImage: `url(${props.imageSrc})`}}>
<p className={Styles.localisationCard}><span className="material-icons">location_on</span>{props.localisation}</p>
</a>
</Link>
<div className={Styles.favCard + " " + ((favoris === true) && Styles.active)} onClick={() => handleClick()}></div>
<p className={Styles.titleCard}>{props.title}</p>
<p className={Styles.pricesCard}><span className="material-icons-outlined">local_offer</span>Prix : {props.prices}</p>
<p className={Styles.eligibilitesCard}><span className="material-icons-outlined">check_circle</span>Eligibilité ({props.eligibilities})</p>
</div>
)
}

Related

Next JS SSG Page shows "found page without a React Component as default export" while deploying in vercel

Just to mention that the answers in this Question did not work for me.
So I have a page which used Static Site Generation(SSG) and no matter how i export it, while deploying in vercel the error always shows "found page without a React Component as default export".
This is my whole file:
File name : [productId].js
import Head from "next/head";
import React from "react";
import ProductDetails from "../../components/Product/ProductDetails/ProductDetails";
export default function index({ product }) {
return (
<>
{/* <Head>
</Head> */}
<ProductDetails product={product} />
</>
);
}
export async function getStaticPaths() {
const res = await fetch(
`${process.env.NEXT_PUBLIC_API}/api/v1/user/products?page=${"all"}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
const data = await res.json();
const paths = data.data.map((product) => {
return {
params: { productId: product._id.toString() },
};
});
console.log("paths", paths);
return {
paths: paths,
fallback: false,
};
}
export const getStaticProps = async (context) => {
const { params } = context;
console.log("params", params);
const { productId } = params;
const res = await fetch(
`${process.env.NEXT_PUBLIC_API}/api/v1/user/products/${productId}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
const data = await res.json();
if (data.status) {
return {
props: {
product: data.data,
},
};
}
};
Can anyone tell me what I am doing wrong here. Becuase I have used this same way of exporting which is "export default function index" in other files and they don't seem to have a problem but only in this file I am having this issue.

Component is being rendered twice in SSG page in Next.js

I'm currently trying using utterances, which is a github-based open source for comments.
I'm using utterances in my SSG page. Therefore, I'm using client side rendering for getting the utterances component.
Here is the code.
// blog/[id].tsx
/* eslint-disable react/no-danger */
import axios from 'axios';
import { dateFormat } from '_Utils/Helper';
import MarkdownRenderer from '_Components/MarkdownRenderer';
import Comment from '_Components/Comment';
import styles from './blog.module.scss';
const Article = ({ article }: any) => {
return (
<div className={styles.container}>
<div className={styles.header}>
<p className={styles.tag}>{article.data.attributes.tag.data.attributes.tag}</p>
<h1>{article.data.attributes.title}</h1>
<p className={styles.publishedDate}>Published at {dateFormat(article.data.attributes.publishedAt)}</p>
</div>
<main
>
<MarkdownRenderer markdown={article.data.attributes.content} />
<Comment />
</main>
</div>
);
};
export async function getStaticPaths() {
const articlePaths: any = await axios.get(`${process.env.NEXT_PUBLIC_BASE_URL}/api/articles/?populate[0]=*`);
const paths = articlePaths.data.data.map((path: any) => ({
params: { id: `${path.id}` },
}));
return { paths, fallback: false };
}
export async function getStaticProps(ctx: any) {
const { params } = ctx;
const { id } = params;
const article = await axios.get(
`${process.env.NEXT_PUBLIC_BASE_URL}/api/articles/${id}?populate[1]=tag&populate[0]=thumbnail`
);
return {
props: { article: article.data },
};
}
export default Article;
// Comment
const Comment = () => {
return (
<section
style={{ height: '350px', width: '100%' }}
ref={(elem) => {
if (!elem) {
return;
}
const scriptElem = document.createElement('script');
scriptElem.src = 'https://utteranc.es/client.js';
scriptElem.async = true;
scriptElem.setAttribute('repo', 'usernamechiho/Cobb-dev-blog');
scriptElem.setAttribute('issue-term', 'title');
scriptElem.setAttribute('theme', 'github-light');
scriptElem.setAttribute('label', 'comment');
scriptElem.crossOrigin = 'anonymous';
elem.appendChild(scriptElem);
}}
/>
);
};
export default Comment;
and the result
I was wondering why it happens and tried dynamic import with ssr: false.
However, there was nothing but the same.
Is there anything I can look for to get through this?

How to pass query params to a redirect in NextJS

I redirect users to the login page, when they try to access a page without authentication.
I then wanna show them a Message.
But I am not able to pass parameters in the redirect. What causes the issue and how to do it properly?
// PAGE NEEDS AUTH / REDIRECT TO LOGIN WITH MESSAGE
// application
import { GetServerSideProps } from 'next';
import SitePageProducts from '../../components/site/SitePageProducts';
import axios from 'axios';
import { getSession } from 'next-auth/react';
import url from '../../services/url';
import { ProductFields } from '../../lib/ebTypes';
function Page() {
return <SitePageProducts />;
}
export default Page;
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getSession(context)
if (session) {
const products = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/products`, {
}).then(res => {
console.log('res :>> ', res);
return res.data.products as ProductFields[]
}).catch(err => console.log(err));
console.log('products :>> ', products);
return {
props: {
loading: true,
token: session.user.token,
}
}
} else {
return {
redirect: {
permanent: false,
destination: url.accountSignIn().href,
props: { test: "Message from inside Redirect" }
},
props: {
params: { message: "Message from inside props" },
query: {
message: 'Message from inside props query'
},
message: 'Message from inside props root'
},
};
}
}
// LOGIN PAGE, SHOULD CONSUME AND SHOW MESSAGE WHY LOGIN IS NEEDED
import { GetServerSideProps } from 'next';
import AccountPageLogin from '../../components/account/AccountPageLogin';
import url from '../../services/url';
import { getSession } from "next-auth/react"
function Page(props: any) {
return <AccountPageLogin {...props} />;
}
export default Page;
export const getServerSideProps: GetServerSideProps = async (ctx) => {
// ALL CTX queries / props are empty?????
// CURRENT: query:{} --- EXPECTING: query: {message: "MY MESSAGE"}
console.log('ctx accountpagelogin::: :>> ', ctx);
const session = await getSession(ctx)
if (session) {
return {
redirect: {
destination: url.accountDashboard().href,
permanent: false,
},
};
}
return {
props: {},
};
};

Redux actions without return or dispatch

I am implementing Oauth from google with redux, and I wanted to have all google API calls handled from my redux and ended up writing helper functions in my actions file that doesn't return anything or call dispatch. I ended up with code where I only dispatch once from my JSX file and wondering if this is okay or there is another better way to do it?
The code is as follows:
authActions.js
const clientId = process.env.REACT_APP_GOOGLE_OAUTH_KEY;
let auth;
export const authInit = () => (dispatch) => {
window.gapi.load('client:auth2', () =>
window.gapi.client.init({ clientId, scope: 'email' }).then(() => {
auth = window.gapi.auth2.getAuthInstance();
dispatch(changeSignedIn(auth.isSignedIn.get()));
auth.isSignedIn.listen((signedIn) => dispatch(changeSignedIn(signedIn)));
})
);
};
export const signIn = () => {
auth.signIn();
};
export const signOut = () => {
auth.signOut();
};
export const changeSignedIn = (signedIn) => {
const userId = signedIn ? auth.currentUser.get().getId() : null;
return {
type: SIGN_CHANGE,
payload: { signedIn, userId },
};
};
GoogleAuth.jsx
import { useSelector, useDispatch } from 'react-redux';
import classNames from 'classnames';
import { authInit, signIn, signOut } from '../../actions/authActions';
function GoogleAuth() {
const { signedIn } = useSelector((state) => state.auth);
const dispatch = useDispatch();
useEffect(() => {
dispatch(authInit());
}, [dispatch]);
const onClick = () => {
if (signedIn) {
signOut();
} else {
signIn();
}
};
let content;
if (signedIn === null) {
return null;
} else if (signedIn) {
content = 'Sign Out';
} else {
content = 'Sign In';
}
return (
<div className="item">
<button
className={classNames('ui google button', {
green: !signedIn,
red: signedIn,
})}
onClick={onClick}
>
<i className="ui icon google" />
{content}
</button>
</div>
);
}
export default GoogleAuth;
The code works fine, but it feels like it might be misleading having action calls in JSX but not dispatching it, is it okay?

Async React-select with redux

I am trying to make an async react-select component with redux but somehow not able to get search results in the dropdown. Very new to this. Please help :)
import React, { PropTypes } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import Select from 'react-select';
import { fetchInstitutionsIfNeeded } from '../../actions/institutions';
class Signup extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null
};
this.getInstitutions = this.getInstitutions.bind(this);
this.onChange = this.onChange.bind(this);
}
onChange(input) {
this.setState({
value: input
});
}
getInstitutions(input) {
const { dispatch } = this.props;
if (!input) {
return Promise.resolve({ options: [] });
}
dispatch(fetchInstitutionsIfNeeded(input));
}
render() {
let options = this.props.options;
return (
<div>
<Select.Async
name="institute"
value={this.state.value}
autoload={false}
onChange={this.onChange}
loadOptions={this.getInstitutions}
/>
</div>
);
}
}
const mapStateToProps = (state) => ({
options: state.institutions.options
});
export default connect(mapStateToProps)(Signup);
Also options object is also properly formatted and is getting updated properly in redux store but not reflecting back in select async's dropdown.
Try this, we can also return from action but it breaks the whole idea of reducers.
// actions.js
export const getProducts = (input = '') => async (dispatch) => {
dispatch({
type: GET_PRODUCTS_PENDING,
payload: {},
});
try {
const response = await axios.get(`/api/v1/products/`);
dispatch({
type: GET_PRODUCTS_SUCCESS,
payload: response.data,
});
} catch (error) {
// handle errors
}
};
// components.jsx
class Signup extends PureComponent {
async getProductsForSelect(input) {
await this.props.getProducts(input);
return this.props.product.options;
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<AsyncSelect
isMulti
cacheOptions
defaultOptions
loadOptions={(e) => this.getProductsForSelect(e)}
/>
</form>
);
}
}

Resources