I'm using a 3rd party API https://www.metaweather.com and in my package.json i've added
"proxy": "https://www.metaweather.com",
My app.js is as follows:
import { createContext, useState } from "react";
import LocationSearch from "./components/locationSearch";
import MainWeather from "./components/mainWeather";
import ExtraWeather from "./components/ExtraWeather";
export const DisplayContext = createContext({
display: false,
setDisplay: () => {},
});
function App() {
const [woeid, setWoeid] = useState(null);
const [display, setDisplay] = useState(false);
return (
<DisplayContext.Provider value={{ display, setDisplay }}>
<LocationSearch setWoeid={setWoeid} />
<MainWeather woeid={woeid} />
<ExtraWeather />
</DisplayContext.Provider>
);
}
export default App;
my LocationSearch.jsx:
import React, { useContext, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";
const LocationSearch = ({ setWoeid }) => {
const [data, setData] = useState({
location: "",
});
const { setDisplay } = useContext(DisplayContext);
function submit(e) {
e.preventDefault();
axios
.get(
// "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/search/?query=" +
"/api/location/search/?query=" +
data.location,
{
location: data.location,
}
)
.then((res) => {
console.log(res.data[0].woeid);
setWoeid(res.data[0].woeid);
setTimeout(() => setDisplay(true), 5000);
})
.catch((err) => {
console.log(err);
});
}
function handle(e) {
const newdata = { ...data };
newdata[e.target.id] = e.target.value;
setData(newdata);
console.log(newdata);
}
return (
<div className="flex w-96 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<form className="flex w-96 mx-auto p-3 rounded-xl bg-white">
<div>
<input
className="text-gray-700"
onChange={(e) => handle(e)}
id="location"
value={data.location}
placeholder="Search for location"
type="text"
/>
<button
className="bg-blue-900 text-gray-300 py-3 px-5 ml-12 rounded-xl"
type="submit"
onClick={(e) => submit(e)}
>
Search
</button>
</div>
</form>
</div>
);
};
export default LocationSearch;
my MainWeather.jsx:
import React, { useContext, useEffect, useState } from "react";
import axios from "axios";
import { DisplayContext } from "../App";
import Loader from "react-loader-spinner";
const MainWeather = ({ woeid }) => {
const [temp, setTemp] = useState([]);
const [icon, setIcon] = useState("");
const { display } = useContext(DisplayContext);
const [load, setLoad] = useState(false);
useEffect(() => {
axios
.get(
// "https://cors-anywhere.herokuapp.com/https://www.metaweather.com/api/location/" +
"/api/location/" +
woeid
)
.then((res) => {
setLoad(true);
console.log(res.data[0]);
setIcon(res.data.consolidated_weather[0].weather_state_abbr);
setTemp((prev) => {
return [
...prev,
res.data.consolidated_weather[0].the_temp,
res.data.consolidated_weather[0].min_temp,
res.data.consolidated_weather[0].max_temp,
res.data.consolidated_weather[0].weather_state_name,
];
});
})
.catch((err) => {
console.log(err);
});
}, [woeid]);
return (
<>
{display && (
<div className="w-96 flex flex-col mx-auto p-3 mt-2 rounded-xl bg-blue-300">
<img
src={"/static/img/weather/" + icon + ".svg"}
alt="Current weather icon"
className="w-40 mx-auto pb-4"
/>
<p className="mx-auto text-5xl pb-3">{Math.round(temp[0])}°C</p>
<p className="mx-auto pb-1">
{Math.round(temp[1])} / {Math.round(temp[2])}
</p>
<p className="mx-auto pb-2">{temp[3]}</p>
</div>
)}
{!display && (
<div>
{load && (
<div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<Loader
className="m-auto"
type="Puff"
color="#00BFFF"
height={100}
width={100}
timeout={5000}
/>
</div>
)}
{!load && (
<div className="flex w-96 h-80 mx-auto mt-5 p-3 rounded-xl bg-blue-300">
<h1 className="m-auto">Please enter a location</h1>
</div>
)}
</div>
)}
</>
);
};
export default MainWeather;
The ExtraWeather.jsx isn't relevant.
If I comment out the MainWeather and log the return from the LocationSearch it returns to object perfectly but as soon as I introduce the MainWeather back I get "CORS header ‘Access-Control-Allow-Origin’ missing" error. I've tried everything I can find from hosting the app on Netlify, changing what is the proxy to the local host address, moving things to different places, and I'm unsure if I did it correctly but I did try a reverse proxy.
Also using herokuapp and a browser extension does fix the problem but I want something more permanent.
Any help will be greatly appreciated.
The issue is that the response is being redirected to include a / suffix, ie
HTTP/2 301
location: https://www.metaweather.com/api/location/44418/
This causes your browser to re-attempt the request to that URL which bypasses your proxy.
Try including the / suffix, eg
axios.get(`/api/location/${woeid}/`)
Keep in mind that the proxy setting only works for local development. If you're deploying to Netlify, see https://docs.netlify.com/routing/redirects/rewrites-proxies/#proxy-to-another-service
Debugging Process
Something was directing your browser to try and access the API by its full URL so I suspected a redirect.
I just ran
curl -v "https://www.metaweather.com/api/location/44418" -o /dev/null
and looked at the response status and headers...
> GET /api/location/44418 HTTP/2
> Host: www.metaweather.com
< HTTP/2 301
< location: https://www.metaweather.com/api/location/44418/
Spotting the difference was the hard part 😄
You could probably have seen something similar in your browser dev-tools Network panel; first a request to /api/location/44418 with a 301 response and location header, then a request to https://www.metaweather.com/api/location/44418/ which failed CORS checks
Related
I don't understand why my useEffect is not being called? I'm following a Youtube tutorial right now and all my code looks exactly the same as in the video. I was reading this: useEffect not being called and not updating state when api is fetched but I couldn't relate it to my problem so I was wondering if anyone could help me on this.
Thanks so much.
import { useSession } from "next-auth/react";
import { ChevronDownIcon } from "#heroicons/react/outline";
import { useEffect, useState } from "react";
import { shuffle } from "lodash";
const colors = [
"from-indigo-500",
"from-blue-500",
"from-green-500",
"from-red-500",
"from-yellow-500",
"from-pink-500",
"from-purple-500",
];
function Center() {
const { data: session } = useSession();
const [color, setColor] = useState(null);
useEffect(() => {
console.log("useEffect called");
setColor(shuffle(colors).pop());
}, []);
return (
<div className="flex-grow">
<header className="absolute top-5 right-8">
<div className="flex items-center bg-red-300 space-x-3 opacity-90 hover:opacity-80 cursor-pointer rounded-full p-1 pr-2">
<img
className="rounded-full w-10 h-10"
src={session?.user.image}
alt=""
/>
<h2>{session?.user.name}</h2>
<ChevronDownIcon className="h-5 w-5" />
</div>
</header>
<section
className={
"flex items-end space-x-7 bg-gradient-to-b to-black ${colors} h-80 text-white padding-8"
}
>
<h1>hello</h1>
</section>
</div>
);
}
export default Center;
MRE:
import { useEffect, useState } from "react";
const colors = [
"from-indigo-500",
"from-blue-500",
"from-green-500",
"from-red-500",
"from-yellow-500",
"from-pink-500",
"from-purple-500",
];
function Center() {
const [color, setColor] = useState(null);
useEffect(() => {
console.log("useEffect called");
}, []);
return (
<div className="flex-grow">
<section
className={
"flex items-end space-x-7 bg-gradient-to-b to-black ${colors} h-80 text-white padding-8"
}
>
</section>
</div>
);
}
export default Center;
FINALLY SOLVED!!!
${colors} should've been ${color} and everything in className={} needs to be surrounded by `` not "". I originally thought useEffect() wasn't even been called because I was looking at VSCode terminal instead of chrome console.
Maybe sounds obvious, but are you sure you didn't filter out your console log from chrome developer tools? The default shows info, warnings and errors but I often filter one and next time I open the console have to reset it.
I keep having errors trying to deploy my Nextjs app to vercel:
Error occurred prerendering page "/". Read more: https://nextjs.org/docs/messages/prerender-error
TypeError: (0 , react_development_.useState) is not a function or its return value is not iterable
at Categories (/vercel/path0/.next/server/chunks/930.js:122:72)
at d (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:498)
at bb (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:16)
at a.b.render (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:42:43)
at a.b.read (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:83)
at Object.exports.renderToString (/vercel/path0/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:52:138)
at Object.renderPage (/vercel/path0/node_modules/next/dist/server/render.js:686:46)
at Object.defaultGetInitialProps (/vercel/path0/node_modules/next/dist/server/render.js:316:51)
at Function.getInitialProps (/vercel/path0/.next/server/pages/_document.js:514:20)
at Object.loadGetInitialProps (/vercel/path0/node_modules/next/dist/shared/lib/utils.js:69:29)
I tried npm run build locally and I get this error:
> Build error occurred
Error: Export encountered errors on following paths:
/
/post/[slug]: /post/first-post
/post/[slug]: /post/how-html-css-js-work
/post/[slug]: /post/nextjs
/post/[slug]: /post/react-hooks
So I assume it has to be something inside my index page and /post/[slug] page. I tried everything like setting getStaticPaths fallback to false and using optional chaining everywhere but I still get the error.
Can someome please help me out, it is so depressing when I finished the project and I can run it in my localhost but and failed in the deployment/build time.
My / page:
import Head from "next/head";
import { PostCard, Categories, PostWidget } from "../components";
import { getPosts } from "../services";
export default function Home({ posts }) {
const sortedPosts = posts.sort(
(a, b) => new Date(b.node.createdAt) - new Date(a.node.createdAt)
);
return (
<>
<Head>
<title>JBLOG | Home</title>
</Head>
<section className="bg-zinc-200 dark:bg-gray-500 transition-colors px-5">
<div className="max-w-7xl mx-auto py-10">
<div className="grid grid-cols-1 md:grid-cols-12 gap-12">
<div className="md:col-span-8 col-span-1 ">
{sortedPosts?.map((post) => (
<PostCard key={post.node.title} post={post.node} />
))}
</div>
<div className="md:col-span-4 col-span-1">
<div className="md:sticky relative md:top-[110px]">
<PostWidget />
<Categories />
</div>
</div>
</div>
</div>
</section>
</>
);
}
export async function getStaticProps() {
const posts = (await getPosts()) || [];
return {
props: {
posts,
},
};
}
My /post/slug page:
import React from "react";
import Head from "next/head";
import { getPosts, getPostDetails } from "../../services";
import {
PostDetail,
Categories,
PostWidget,
Author,
Comments,
CommentsForm,
} from "../../components";
import { useRouter } from "next/router";
const Post = ({ post }) => {
const router = useRouter();
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<>
<Head>
<title>{post?.title}</title>
</Head>
<section className="bg-zinc-200 dark:bg-gray-500 transition-colors px-5">
<div className="max-w-7xl mx-auto py-10">
<div className="grid grid-cols-1 md:grid-cols-12 md:gap-6">
<div className="md:col-span-8 col-span-1 ">
<PostDetail post={post} />
<CommentsForm slug={post?.slug} />
<Comments slug={post?.slug} />
</div>
<div className="md:col-span-4 col-span-1">
<div className="md:sticky relative md:top-[110px]">
<Author author={post?.author} />
<PostWidget
slug={post?.slug}
categories={post?.categories?.map(
(category) => category.slug
)}
/>
<Categories />
</div>
</div>
</div>
</div>
</section>
</>
);
};
export default Post;
export const getStaticProps = async (context) => {
const data = await getPostDetails(context.params.slug);
return {
props: {
post: data,
},
};
};
export const getStaticPaths = async () => {
const posts = await getPosts();
return {
paths: posts.map((post) => ({ params: { slug: post.node.slug } })),
fallback: true,
};
};
what's your npm run build?
https://nextjs.org/docs/basic-features/data-fetching
fallback: true is not supported when using next export.
I have built an invoice dashboard system using React. I have another version of the app that is built with React and I run into the same issue.
I have commented out all calls to my API, the useEffect(), and it still reloads the page every second. Any suggestions would be helpful.
Invoice-dashboard
import React, { Fragment, useState, useEffect } from 'react'
import { useAuth0 } from "#auth0/auth0-react";
import axios from 'axios';
import LogRocket from 'logrocket';
import 'react-notifications/lib/notifications.css';
import {NotificationContainer, NotificationManager} from 'react-notifications';
import { Menu, Transition } from '#headlessui/react'
import {
BellIcon,
MenuAlt2Icon
} from '#heroicons/react/outline'
import { SearchIcon } from '#heroicons/react/solid'
//Material UI
import { makeStyles } from "#material-ui/core/styles";
import Button from '#material-ui/core/Button';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import StorageIcon from '#material-ui/icons/Storage';
import MessageIcon from '#material-ui/icons/Message';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import styles from "../css/jss/material-dashboard-react/views/dashboardStyle";
//components
import Card from '../Tailwind-Components/Card';
import Snackbar from '../Material-Components/Snackbar';
import Sidebar from '../Tailwind-Components/Sidebar';
const userNavigation = [
{ name: 'Your Profile', href: '#' },
{ name: 'Settings', href: '#' },
{ name: 'Sign out', href: '#' },
]
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function InvoiceDashboard() {
axios.defaults.withCredentials = true;
const [sidebarOpen, setSidebarOpen] = useState(false)
// Abstracted Authentication variables
const { user, isAuthenticated, isLoading }= useAuth0();
// User Invoices
const [data, setData] = useState([{}]);
// Users Paid Invoices
const [paidData, setPaidData] = useState([{}]);
// The currently logged in user to be sent to server
const [loggedInUser, setLoggedInUser] = useState({});
// Positioning State for notification
const [tc, setTC] = useState(true);
const [bl, setBL] = useState(true);
// Tabs state
const [value, setValue] = useState(0);
// Modal open state
const [open, setOpen] = useState(true);
// Styling
const useStyles = makeStyles(styles);
// Time Conversions
const moment = require('moment');
moment().format();
// Calls the backend to capture the users open invoices
async function fetchUserInvoices() {
try {
// Making a call to external api
const url = `${process.env.GATSBY_FETCH_USER_INVOICES}`;
const axiosConfig = {
method: 'get',
url,
};
const invoiceResponse = await axios(axiosConfig).catch((e) => { console.log(e) });
setData(invoiceResponse?.data);
} catch (error) {
console.log('[fetchUserInvoices] An Error has occured:', error);
}
return;
}
// Calls the backend to capture the users paid invoices
async function fetchPaidInvoices() {
try {
const url = `${process.env.GATSBY_FETCH_PAID_INVOICES}`;
const axiosConfig = {
method: 'get',
url,
};
const invoices = await axios(axiosConfig).catch((e) => {console.log(e)});
await setPaidData(invoices.data);
console.log('[fetchPaidInvoices] Paid invoices for user fetched from server');
} catch (error) {
console.log('[fetchPaidInvoices]: An Error has occured', error)
}
}
// calls the backend and sends the current user object
async function sendLoggedInUser(user) {
try {
const url = `${process.env.GATSBY_SAVE_USER}`;
const axiosConfig = {
method: 'post',
url,
data: user
};
await axios(axiosConfig).catch((e) => {console.log(e)});
console.log('[sendLoggedInUser]: LoggedInUser sent to server');
return;
} catch (error) {
console.log('[sendLoggedInUser]: An Error has Occured', error);
return;
}
};
// Works just as the function above only the paid invoices endpoint utilizes the information
async function sendPaidInvoiceUser(current) {
try {
const url = `${process.env.GATSBY_SEND_PAID_INVOICE_USER}`;
const axiosConfig = {
method: 'post',
url,
data: current
};
await axios(axiosConfig).catch((e) => {console.log(e)});
console.log('[sendPaidInvoiceUser]: paid Invoice User sent to server');
return;
} catch (error) {
console.log('[sendPaidInvoiceUser]: An Error has Occured', error);
return;
}
};
// Calls the backend endpoint that gets the invoices from stripe and the DB and activates the logic
async function updateDB() {
try {
const url = `${process.env.GATSBY_STORE_INVOICES}`;
const axiosConfig = {
method: 'get',
url,
};
await axios(axiosConfig).catch((e) => {console.log(e)});
} catch (error) {
console.log('[updateDB]: An Error has occured', error)
}
};
// Closes the Invoice reminders
const handleClose = () => {
setOpen(false);
};
// saves the users preference for reminders to local Storage as a Boolean
function userPreference(data){
localStorage.setItem('notification', data);
};
// Retreives the users preference from localStorage and executes a conditional based on the Boolean
function userPreferenceGet() {
const preference = localStorage.getItem('notification');
if(preference){
console.log('[userPreferenceGet] Preference:', preference)
return true;
} else {
console.log('[userPreferenceGet] Preference:', preference)
return false;
}
};
// Holds the UI of the alert that allows the User to choose their reminder notifcation preference
function notifcationSettings() {
return (
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Would you like Invoice reminders?"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Let 02Pilot remind you of invoices that are due in 2 days! We promise we wont spam you!
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
id="notificationYes"
onClick={() => {
handleClose()
userPreference(true)
}}
color="primary">
Yes 😃
</Button>
<Button
id="notificationNo"
onClick={() => {
handleClose()
userPreference(false)
}}
color="secondary">
No 😞
</Button>
</DialogActions>
</Dialog>
)
}
useEffect(() => {
// App rerenders if the user is authenticated.
if (isAuthenticated) {
setLoggedInUser(user);
}
}, [ isAuthenticated])
try {
// sends call to backend to update database with any invoices that may be paid.
updateDB();
// executes POST call to backend with the logged in user.
sendLoggedInUser(loggedInUser);
sendPaidInvoiceUser(loggedInUser);
} catch (error) {
console.log(error);
}
setTimeout(() => {
// executes function that fetches invoices from DB after 100 milliseconds. Had to account for invoices to get fetched.
fetchUserInvoices();
}, 100)
// Function that controls notifications.
const showNotification = (place) => {
switch (place) {
case "bl":
if (bl) {
setTimeout(function() {
setBL(false);
}, 2000);
}
break;
default:
break;
}
};
// Tab handle change function
const handleChange = (event, newValue) => {
setValue(newValue);
if(newValue == 1) {
//fetchPaidInvoices();
return;
} else {
return;
}
};
// Controls Tabs panel
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box p={3}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
// Controls index of tabs
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
// Function adds a comma and a decimal to numbers that need it.
function decimalInsert(num) {
const internationalNumberFormat = new Intl.NumberFormat('en-US')
var num = (num/100).toFixed(2);
return internationalNumberFormat.format(num)
};
// Check the size of an object, has the same functionality of Array.length()
Object.size = function(obj) {
var size = 0,
key;
for (key in obj) {
if (obj.hasOwnProperty(key)) size++;
}
return size;
};
// The conditional checks the size of the object and if the Users information exists then it is sent to LogRocket
if(process.env.NODE_ENV === 'production'){
if(Object.size(loggedInUser) > 0 ) {
LogRocket.identify(loggedInUser.sub, {
name: loggedInUser.name,
email: loggedInUser.email
});
}
}
// Function that sorts the invoices from newest to oldest
function biggestToSmallest(a, b) {
return b.created - a.created;
}
// Shows the reminder for the invoices that are due in 2 days
function reminderNotification(data) {
// Converting epoch seconds to human readable date
const invoiceDue = moment.unix(data.due_date);
// Subtracting 2 days from the due date (for the reminder)
const reminderDate = invoiceDue.subtract(2, 'days');
// Change the format of the date
const reminderConverted = reminderDate.format(' MMMM Do YYYY');
// Get the current date
const currentDate = moment();
// Change the format of the current date
const currentDateConverted = currentDate.format(' MMMM Do YYYY');
if(reminderConverted === currentDateConverted ) {
return (
NotificationManager.warning(`${data.id} is due in 2 days: Please visit the Dashboard 😃`, 'Invoices Due', 2000)
)
} else {
console.log('[card] No invoices are due at this time');
}
return;
};
// Orders invoices by newest to oldest
if(data && paidData) {
data.sort(biggestToSmallest);
paidData.sort(biggestToSmallest);
}
function card(data) {
// Converting epoch seconds to human readable date
const invoiceCreated = moment.unix(data.created);
const invoiceCreatedConverted = invoiceCreated.format(' MMMM Do YYYY');
const invoiceDue = moment.unix(data.due_date);
const invoiceDueConverted = invoiceDue.format(' MMMM Do YYYY');
// Executing the function to convert amout due.
const amountDue = decimalInsert(data.amount_due);
// Shows a notification when data from database has been loaded
return(
<div>
{ data ? showNotification("bl") : null}
<Card
invoiceId={data.id}
invoiceCreated={invoiceCreatedConverted}
invoiceDue={invoiceDueConverted}
amountDue={amountDue}
invoiceUrl={data.hosted_invoice_url}
invoicePdf={data.invoice_pdf}
/>
{userPreferenceGet() === true ? reminderNotification(data) : console.log('{User Settings} User has chosen to not have reminders')}
</div>
);
};
// Shows the paid invoices when the apropriate tab is selected
function paidCard(paidData) {
// Converting epoch seconds to human readable date
const invoiceCreated = moment.unix(paidData.created);
const invoiceCreatedConverted = invoiceCreated.format(' MMMM Do YYYY');
const invoiceDue = moment.unix(paidData.due_date);
const invoiceDueConverted = invoiceDue.format(' MMMM Do YYYY');
// Executing the function to convert amout due.
const amountDue = decimalInsert(paidData.amount_due);
// Shows a notification when data from database has been loaded
return(
<Card
invoiceId={paidData.id}
invoiceCreated={invoiceCreatedConverted}
invoiceDue={invoiceDueConverted}
amountDue={amountDue}
invoicePdf={paidData.invoice_pdf}
/>
);
};
let invoiceData = []
let paidInvoiceData = []
// Render the invoices by mapping through state.
if(data && paidData && Array.isArray(data) && Array.isArray(paidData)) {
invoiceData = data.map(card);
paidInvoiceData = paidData.map(paidCard);
}
// If the User is still logging in then this DIV will render
if(isLoading || !user) {
return <div>Loading...</div>
}
return (
isAuthenticated && (
<div className=" h-screen flex overflow-hidden ">
<Sidebar />
<div className=" flex flex-col w-0 flex-1 overflow-hidden">
{userPreferenceGet() === false ? notifcationSettings() : console.log( '{Invoice Alert Settings} Alerts have been muted') }
<div className="relative flex-shrink-0 flex h-16 bg-white shadow">
<button
type="button"
className="px-4 border-r border-gray-200 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 md:hidden"
onClick={() => setSidebarOpen(true)}
>
<span className="sr-only">Open sidebar</span>
<MenuAlt2Icon className="h-6 w-6" aria-hidden="true" />
</button>
<div className="flex-1 px-4 flex justify-between">
<div className="flex-1 flex">
<form className="w-full flex md:ml-0" action="#" method="GET">
<label htmlFor="search-field" className="sr-only">
Search
</label>
<div className="relative w-full text-gray-400 focus-within:text-gray-600">
<div className="absolute inset-y-0 left-0 flex items-center pointer-events-none">
<SearchIcon className="h-5 w-5" aria-hidden="true" />
</div>
<input
id="search-field"
className="block w-full h-full pl-8 pr-3 py-2 border-transparent text-gray-900 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-0 focus:border-transparent sm:text-sm"
placeholder="Search"
type="search"
name="search"
/>
</div>
</form>
</div>
<div className="ml-4 flex items-center md:ml-6">
<button
type="button"
className="bg-white p-1 rounded-full text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
<span className="sr-only">View notifications</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>
{/* Profile dropdown */}
<Menu as="div" className="ml-3 relative z-50">
<div>
<Menu.Button className="max-w-xs bg-white flex items-center text-sm rounded-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full"
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
alt=""
/>
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
{userNavigation.map((item) => (
<Menu.Item key={item.name}>
{({ active }) => (
<a
href={item.href}
className={classNames(active ? 'bg-gray-100' : '', 'block px-4 py-2 text-sm text-gray-700')}
>
{item.name}
</a>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
</div>
</div>
</div>
<main className="flex-1 relative overflow-y-auto focus:outline-none bg-gray-50">
<div className="py-6">
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8">
<h1 className="text-2xl font-semibold text-gray-900">Dashboard</h1>
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 flex mt-40">
{/* Replace with your content */}
<div className="py-4">
<Tabs value={value} onChange={ handleChange} aria-label="invoices tab">
<Tab label="Open Invoices" {...a11yProps(0)} />
<Tab label="Paid Invoices" {...a11yProps(1)} />
</Tabs>
<TabPanel value={value} index={0}>
{data ? invoiceData : <div>Invoices could not be retrieved at this time. PLease try to refresh or contact your Administrator.</div>}
</TabPanel>
<TabPanel value={value} index={1}>
{paidData ? paidInvoiceData : <h1>Invoices could not be retrieved at this time. PLease try to refresh or contact your Administrator.</h1>}
</TabPanel>
<Snackbar
id={`user-welcome-snack`}
place="tc"
color="info"
icon={MessageIcon}
message={`Welcome ${user.nickname} 😀
You have ${data.length} open Invoices`}
open={tc}
closeNotification={() => setTC(false)}
close
/>
<Snackbar
id={`fetch-from-db-snack`}
place="bl"
color="success"
icon={StorageIcon}
message="Invoices Successfully fetched from Database."
open={bl}
closeNotification={() => setBL(false)}
close
/>
<NotificationContainer/>
</div>
{/* /End replace */}
</div>
</div>
</main>
</div>
</div>
)
)
}
You are calling fetchUserInvoices in setTimeout and in the response your are setting the data, might be the reason.
I'm developing a blog on next.js with sanity.io, and I'm having trouble using the code-input plugin.
What I do have
I'm able to use the code component block on sanity, which looks something like this:
Everything good on the sanity side. My problem comes with using it on the next.js [slug].js file.
I have this error prompt out:
This issue with this is that I don't have a serializer.js file/component anywhere on my code, not even on the studio root folder. I've seen this applies for gatsby but I don't know how to apply it for Next.js
This is what I currently Have:
import groq from 'groq'
import imageUrlBuilder from '#sanity/image-url'
import BlockContent from '#sanity/block-content-to-react'
import client from '../../client'
import Layout from '../../components/layout'
import utilStyles from '../../styles/utils.module.css'
import styles from '../../components/layout.module.css'
function urlFor (source) {
return imageUrlBuilder(client).image(source)
}
const Post = (props) => {
const {
title = 'Missing title',
name = 'Missing name',
categories,
authorImage,
mainImage,
code,
body = []
} = props
console.log(props)
return (
<Layout>
<article>
<div className={styles.container}>
<figure>
<img src={urlFor(mainImage).url()} />
</figure>
<h1 className={utilStyles.headingXl}>{title}</h1>
{categories && (
<ul className="inline">
Category:
{categories.map(category =>
<li key={category}>
<span className="inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-indigo-100 bg-indigo-700 rounded">{category}</span>
</li>)}
</ul>
)}
<BlockContent
blocks={body}
imageOptions={{fit: 'max'}}
{...client.config()}
{...code}
/>
</div>
</article>
</Layout>
)
}
const query = groq ` *[_type == "post" && slug.current == $slug][0]{
title,
"name": author->name,
"categories": categories[]->title,
mainImage,
code,
"authorImage": author->image,
body,
}`
Post.getInitialProps = async function(context) {
const {slug = ""} = context.query
return await client.fetch(query, { slug })
}
export default Post
I really would appreciate some help here! Thanks <3
You can pass a serializer for the code block type to your BlockContent using the serializers prop.
const serializers = {
types: {
code: props => (
<pre data-language={props.node.language}>
<code>{props.node.code}</code>
</pre>
)
}
}
// ...
<BlockContent
blocks={body}
imageOptions={{fit: 'max'}}
{...client.config()}
{...code}
serializers={serializers}
/>
so i have this issue with next.js, When my user tries to go from one profile to another via Link in navbar:
<li>
<Link href={`/profile/${user.user.id}`}>
<a className="flex flex-row items-center">
<BiUserCircle />
<span className="ml-1">Profile</span>
</a>
</Link>
</li>
Next doesn't seem to re-render the profile component which is unfortunate because I'm pulling initial profile data in getServerSideProps which leads to weird behavior like when the initial useState data is saved from last visited profile. How can I ensure that each time user visits the profile page brand new initial data is sent to useState?
My profile page looks like this:
const Profile = ({ initialProfile, posts }) => {
const router = useRouter();
const [profile, setProfile] = useState(initialProfile);
const { userId } = router.query;
const dispatch = useDispatch();
useEffect(() => {
// This is my current solution, however it feels really hacky
fetchProfile();
}, [userId]);
const fetchProfile = async () => {
const resp = await axios.get(apiURL + `accounts/users/${userId}`);
setProfile((prof) => ({ ...prof, ...resp.data }));
};
return (
<Layout>
<ProfileSummary
isUser={isUser}
isFollowed={isFollowed}
handleFollow={handleFollow}
profile={profile}
/>
{isUser ? (
<div className="flex justify-center">
<Button colorScheme="green">Add post</Button>
</div>
) : null}
<div className="w-full flex justify-center flex-col items-center pl-2 pr-2">
{posts
? posts.map((post) => (
<Card
key={post.id}
author={post.author}
likes={post.likes}
desc={post.description}
img={post.image}
isLiked={post.is_liked}
postId={post.id}
comments={post.comments}
/>
))
: "No Posts found"}
</div>
</Layout>
);
};
export async function getServerSideProps(context) {
const { userId } = context.query;
const profileResp = await axios.get(apiURL + `accounts/users/${userId}`);
const postsResp = await axios.get(
apiURL + `posts/?author__id=${profile.id}`
);
const profile = profileResp.data;
const posts = postsResp.data;
return {
props: {
initialProfile: profile,
posts,
},
};
}
export default Profile;
I'd really love any help, maybe I should change my overall approach?
The whole project can be found here:
https://github.com/MaciejWiatr/Nextagram/tree/develop
Dont worry this is an known bug, you can read more about it here https://github.com/vercel/next.js/issues/10400. Hopefully it will be fixed soon.