I found some error when I deploy my app in Vercel. I am displaying a map using MapBox with "react-map-gl" library, in React and Next.js.
The MapBox work fine in local but not displaying in Prod. I can see the Popup and the Markers but not the map (it only shows a gray background).
What could possibly be the problem ?
import { useState, useEffect } from 'react';
import ReactMapGL, { Marker, Popup } from 'react-map-gl';
import getCenter from 'geolib/es/getCenter';
import Image from "next/image"
import Link from "next/link"
function Map({ coordinates, marker }) {
const [viewport, setViewport] = useState({
width: '100%',
height: '100%',
latitude: coordinates.latitude,
longitude: coordinates.longitude,
zoom: coordinates.zoom
});
console.log(marker)
useEffect(() => {
let centeredCoordinate = getCenter(coordinates.length >= 1 ?
coordinates.map(result => { return { latitude: result.latitude, longitude: result.longitude } }) :
{ latitude: 47.15025576066852, longitude: 2.032816514216602, zoom: 5 })
;
if (coordinates.length > 1) {
return setViewport({
width: '100%',
height: '100%',
latitude: centeredCoordinate.latitude,
longitude: centeredCoordinate.longitude,
zoom: 5
})
}
setViewport({
width: '100%',
height: '100%',
latitude: centeredCoordinate.latitude,
longitude: centeredCoordinate.longitude,
zoom: 13
})
}, [coordinates]);
console.log(marker)
return <ReactMapGL
{...viewport}
mapStyle='mapbox://styles/kamarov/ckuiomg715hg317qly3n8zoml'
mapboxApiAccessToken={process.env.MAPBOX_KEY}
onViewportChange={(viewport) => setViewport(viewport)}
>
{coordinates.map(result => (
<div key={result.longitude}>
<Marker
longitude={result.longitude}
latitude={result.latitude}
offsetLeft={-20}
offsetTop={-10}
>
{result.entity.isPremium ?
<p className="text-xl cursor-pointer z-90">⭐</p> :
<p className="text-xl cursor-pointer z-90">📍</p>}
</Marker>
{marker?.map(mark => (
<Popup className="rounded-xl " closeButton={false} latitude={mark.latitude} longitude={mark.longitude}>
{mark.entity.isPremium ?
<div className="flex items-center p-1 justify-between space-x-2">
<div className="w-16 h-12 relative">
<Image src={mark.entity.logo} layout="fill" objectFit="contain" />
</div>
<div className="flex flex-col">
<p className="font-content text-md text-primary">{mark.entity.longName}</p>
<Link href={"/etablissements/" + mark.entity.slug}>
<a className="mr-auto text-sm text-contrast font-content underline hover:text-primary duration-300">
Voir l'établissement
</a></Link>
</div>
</div> :
<div className="flex flex-col justify-center">
<p className="font-content text-md text-primary">{mark.entity.longName}</p>
<Link href={"/etablissements/" + mark.entity.slug}>
<a className="mr-auto text-sm text-contrast font-content underline hover:text-primary duration-300">
Voir l'établissement
</a></Link>
</div>}
</Popup>
))}
</div>
))}
</ReactMapGL>
}
export default Map
I saw in other questions that the solution was to add :
/* eslint-disable import/no-webpack-loader-syntax */
import mapboxgl from 'mapbox-gl';
// #ts-ignore
mapboxgl.workerClass = require('worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker').default;
But it's not working for me as I am using react-map-gl..
Here are the errors showing in the console of the browser in the PROD (the local browser is not showing any error)
Found the solution in my next.config.js I had to remove :
swcMinify: true
And it works just fine in production.
Related
I am using NEXTJS and SanityIO. I am getting this error when adding this Framer motion component? "TypeError: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component" or it just fails to refresh.
My code causing the problem is below and below the code is a picture of my folder layout. If it is helpful to know, the Header component works if I just render an h1 alone. Any help or feedback is greatly appreciated.
import React from 'react'
import { SocialIcon } from 'react-social-icons';
import {motion} from "framer-motion";
type Props = {}
function Header({}: Props) {
return (
<header className="sticky top-0 p-5 flex items-start justify-between max-w-7xl mx-auto z-
20 xl:items-center">
<motion.div
initial={{
x:-500,
opacity:0,
scale:0.5
}}
animate={{
x:0,
opacity: 1,
scale: 1
}}
transition={{
duration:1.5,
}}
className="flex flex-row items-center">
{/*Social Icons*/}
<SocialIcon
url="https://www.youtube.com"
fgColor="gray"
bgColor="transparent"
/>
<SocialIcon
url="https://www.youtube.com"
fgColor="gray"
bgColor="transparent"
/>
<SocialIcon
url="https://www.youtube.com"
fgColor="gray"
bgColor="transparent"
/>
</motion.div>
<motion.div
initial={{
x: 500,
opacity: 0,
scale: 0.5,
}}
animate={{
x:0,
opacity:1,
scale:1,
}}
transition={{duration:1.5}}
className="flex flex-row items-center text-gray-300 cursor-pointer">
<SocialIcon
className="cursor-pointer"
network="email"
fgColor="gray"
bgColor="transparent"
/>
<p className="uppercase hidden md:inline-flex text-sm text-gray-400"> Get In Touch</p>
</motion.div>
</header>
)
}
export default Header
I have a mySQL database with about 100 rows, containing the data of products I'm listing in an eCommerce app.
I have them displayed on the front-end by using Axios to get the rows from my back-end SELECT * statement. They are then held as an array state const [rows, setRows] = useState([]);.
These rows are then mapped for each row found in the table, so around 100 maps.
I have noticed with this many results performance has started to become an issue and I've heard similar issues from mapping arrays.
I've benchmarked and tinkered to find that this component is the one causing the performance issues.
I would like any solutions to improve my component's performance, without altering the structure too much.
Heres the component
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Button from '#mui/material/Button';
export default function ProductCard(props) {
const [rows, setRows] = useState([]);
const isntDesktop = props.isntDesktop; //Constant from parent
const isMobile = window.innerWidth < 768; //Checks if screen is smaller than mobile
const sidebar = props.sidebar; //Sidebar state from parent component
useEffect(() => {
axios.get('http://localhost:8080/products/get/')
.then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
});
return (
rows
.sort((a, b) => a.name.localeCompare(b.name))
//Map products
.map((row, index) => {
return (
/* Adjust structure for mobile */
isMobile ? (
/* Product card */
<div
key={index}
class={`${
sidebar ? 'border-0' : 'border'
} relative mt-6 py-8 px-5 rounded`}
>
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="flex items-center sm:items-start space-x-5">
{/* Image */}
<img
src={row.image}
alt="product"
class={`${
sidebar ? 'opacity-50' : 'opacity-100'
} mt-4 min-w-[125px] sm:w-[300px] h-auto`}
/>
<div class="mt-4">
{/* Price */}
<p class="text-2xl sm:text-xl font-semibold sm:font-medium text-gray-600">£ {row.price}</p>
{/* Keywords */}
<ul class="ml-5 mt-2 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
</div>
</div>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex justify-center space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
<div class="mt-4 w-full mx-auto flex flex-col sm:flex-row space-y-2.5 sm:space-y-0 sm:space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
) : (
<div key={index} class="flex mt-6 py-8 px-5 border rounded">
{/* Image */}
<img src={row.image} alt="product" class="mr-4 w-[300px] h-auto" />
<div class="relative w-full px-5 items-center">
{/* Heading */}
<h2 class="text-2xl font-semibold tracking-wide text-gray-700">{row.name}</h2>
<div class="mt-8 flex justify-between">
{/* Keywords */}
<ul class="ml-5 space-y-1">
<li class="list-disc text-gray-600">{row.keyword_one}</li>
<li class="list-disc text-gray-600" >{row.keyword_two}</li>
<li class="list-disc text-gray-600">{row.keyword_three}</li>
</ul>
{/* Promo */}
<div class="flex flex-col ml-5">
{/* Price */}
<p class="text-xl font-medium text-gray-600">£ {row.price}</p>
<div
class={`${
sidebar ? 'bg-none' : 'bg-gray-100'
} mt-4 py-2 px-4 flex space-x-2.5`}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-sky-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 18.75a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 01-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 00-3.213-9.193 2.056 2.056 0 00-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 00-10.026 0 1.106 1.106 0 00-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12" />
</svg>
<label class="text-gray-600 text-sm">Free delivery on orders over £50</label>
</div>
</div>
</div>
<div class="absolute bottom-0 w-full pr-5 flex space-x-2.5">
{sidebar && isntDesktop ? (
<Button
type="submit"
variant="contained"
sx={{
opacity: 0.5,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
) : (
<Button
type="submit"
variant="contained"
sx={{
opacity: 1,
bgcolor: '#6366f1',
textTransform: 'none',
width: '100%',
':hover': {
backgroundColor: '#4f46e5',
},
}}
>
View product
</Button>
)}
<Button
type="submit"
sx={{
border: ' 1px solid #38bdf8',
color: '#38bdf8',
textTransform: 'none',
width: '100%',
':hover': {
color: '#ffffff',
backgroundColor: '#38bdf8',
},
}}
>
Add to cart
</Button>
</div>
</div>
</div>
)
)
})
)
}
Since you (probably) want to fetch your data once (on mount), you will have to add an empty dependency array to your useEffect to avoid infinitely running a query . You can read more about it here: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects (In the Note section).
useEffect(() => {
axios.get('http://localhost:8080/products/get/').then(res => {
setRows(res.data);
}).catch(err => {
console.log(err);
});
}, [])
I saw that you render all your data on render and unfortunately, React is notoriously slow when it comes to rendering large lists. You might need to think about windowing your data, aka rendering a chunk of your data when your page first load and render more data as the user scroll further down. There's a great library that handles this for you: https://tanstack.com/virtual/v3/docs/guide/introduction.
I hope this helps!
Stack: NextJS, JavaScript, React, Tailwind, Framer Motion
I've scoured the web for answers to this but mostly the answers were saying that the <div> that wraps the image must have a position of relative. I tried this and it didn't change anything.
Code for the text and images:
import React from "react";
import { motion } from "framer-motion";
import Image from "next/image";
export default function Card({ image, name, description }) {
return (
<motion.div
className="flex justify-center mt-5"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.3, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
<div className="relative">
<Image
className="rounded-full absolute"
src={image}
width={100}
height={100}
alt="Leader profile picture"
/>
</div>
<div>
<motion.h1
className="text-xl pl-1 font-bourton"
initial="hidden"
animate="visible"
variants={{
hidden: { scale: 0.9, opacity: 0 },
visible: {
scale: 1,
opacity: 1,
transition: { delay: 0.35, ease: [0.17, 0.67, 0.83, 0.67] },
},
}}
>
{name}
</motion.h1>
<motion.p className="text-sm p-3 text-wrap w-48 text-slate-600 font-bourton">
{description}
</motion.p>
</div>
</motion.div>
);
}
The code for the sidebar:
import { useState } from "react";
import { motion } from "framer-motion";
import classnames from "classnames";
import { useRouter } from "next/router";
const path01Variants = {
open: { d: "M3.06061 2.99999L21.0606 21" },
closed: { d: "M0 9.5L24 9.5" },
};
const path02Variants = {
open: { d: "M3.00006 21.0607L21 3.06064" },
moving: { d: "M0 14.5L24 14.5" },
closed: { d: "M0 14.5L15 14.5" },
};
const Sidebar = () => {
const [showSidebar, setShowSidebar] = useState(false);
const [animation, setAnimation] = useState("closed");
const router = useRouter();
const onClick = () => {
setAnimation("moving");
setTimeout(() => {
setAnimation(animation === "closed" ? "open" : "closed");
}, 200);
setShowSidebar(!showSidebar);
};
return (
<>
{showSidebar ? (
<button
className="flex justify-center relative z-100 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
) : (
<button
className="flex justify-center top-12 right-12 items-center w-12 h-12 border border-black rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#000"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#000"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
)}
<div
className={` bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${
showSidebar ? "translate-x-0" : "translate-x-full"
}`}
>
<button
className="flex justify-center absolute top-8 right-8 items-center w-12 h-12 border border-white rounded-full"
onClick={onClick}
>
<svg width="24" height="24" viewBox="0 0 24 24">
<motion.path
stroke="#fff"
animate={animation}
variants={path01Variants}
/>
<motion.path
stroke="#fff"
animate={animation}
variants={path02Variants}
/>
</svg>
</button>
<h3 className="mt-20 text-4xl underline font-bourton text-white">
<button className="underline" onClick={() => router.push("whoweare")}>
Who We Are
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button
className="underline"
onClick={() => router.push("ourleaders")}
>
Our Leaders
</button>
</h3>
<h3 className="mt-20 text-4xl font-bourton text-white">
<button className="underline">News</button>
</h3>
</div>
</>
);
};
export default Sidebar;
Before opening sidebar
After opening sidebar
Any help would be appreciated! Thank you.
It looks like you're not applying a z-index to the sidebar itself. That <div> is the container that needs to overlap the main content, so that is where you need to add the utility class.
Additionally, it looks like you're trying to use z-100 which is not one of the Tailwind defaults (see: https://tailwindcss.com/docs/z-index). You can either add that new value to your tailwind.config.js or try z-50, which is the highest default.
For example:
<div className={`z-50 bg-unionred border-black opacity-90 top-0 right-0 lg:w-[35vw] sm:w-[50vw] p-10 pl-20 fixed text-white h-full ease-in-out duration-300 ${showSidebar ? "translate-x-0" : "translate-x-full"}`}>
just having issues with populating the JSON into my chart. I'm seeing the response in the console log with API status with no errors, but for some reason, it's not populating it. Using Axios to fetch the example API to populate the emp sal and age. I have a feeling that I need to move the "UseEffect" somewhere but not sure where that needs to go.
import React, { useState, useEffect } from "react";
import { Link } from 'react-router-dom';
import LineChart from '../../charts/LineChart01';
import Icon from '../../images/icon-01.svg';
import EditMenu from '../EditMenu';
import axios from "axios";
// Import utilities
import { tailwindConfig, hexToRGB } from '../../utils/Utils';
const DashboardCard01 = () => {
const [chartData, setChartData] = useState({});
const [employeeSalary, setEmployeeSalary] = useState([]);
const [employeeAge, setEmployeeAge] = useState([]);
const chart = () => {
let empSal = [];
let empAge = [];
axios
.get("http://dummy.restapiexample.com/api/v1/employees")
.then(res => {
console.log(res);
for (const dataObj of res.data.data) {
empSal.push(parseInt(dataObj.employee_salary));
empAge.push(parseInt(dataObj.employee_age));
}
setChartData({
labels: empAge,
datasets: [
{
label: empAge,
data: empSal,
//backgroundColor: ["rgba(75, 192, 192, 0.6)"],
borderWidth: 4
}
]
});
})
.catch(err => {
console.log(err);
});
console.log(empSal, empAge);
datasets: [
// Indigo line
{
data: empAge,
fill: true,
backgroundColor: `rgba(${hexToRGB(tailwindConfig().theme.colors.blue[500])}, 0.08)`,
borderColor: tailwindConfig().theme.colors.indigo[500],
borderWidth: 2,
tension: 0,
pointRadius: 0,
pointHoverRadius: 3,
pointBackgroundColor: tailwindConfig().theme.colors.indigo[500],
clip: 20,
},
// Gray line
{
data: empSal,
borderColor: tailwindConfig().theme.colors.slate[300],
borderWidth: 2,
tension: 0,
pointRadius: 0,
pointHoverRadius: 3,
pointBackgroundColor: tailwindConfig().theme.colors.slate[300],
clip: 20,
},
]
};
useEffect(() => {
chart();
}, []);
return (
<div className="flex flex-col col-span-full sm:col-span-6 xl:col-span-4 bg-white shadow-lg rounded-sm border border-slate-200">
<div className="px-5 pt-5">
<header className="flex justify-between items-start mb-2">
{/* Icon */}
<img src={Icon} width="32" height="32" alt="Icon 01" />
{/* Menu button */}
<EditMenu className="relative inline-flex">
<li>
<Link className="font-medium text-sm text-slate-600 hover:text-slate-800 flex py-1 px-3" to="#0">Option 1</Link>
</li>
<li>
<Link className="font-medium text-sm text-slate-600 hover:text-slate-800 flex py-1 px-3" to="#0">Option 2</Link>
</li>
<li>
<Link className="font-medium text-sm text-rose-500 hover:text-rose-600 flex py-1 px-3" to="#0">Remove</Link>
</li>
</EditMenu>
</header>
<h2 className="text-lg font-semibold text-slate-800 mb-2">Acme Plus</h2>
<div className="text-xs font-semibold text-slate-400 uppercase mb-1">Sales</div>
<div className="flex items-start">
<div className="text-3xl font-bold text-slate-800 mr-2">$24,780</div>
<div className="text-sm font-semibold text-white px-1.5 bg-green-500 rounded-full">+49%</div>
</div>
</div>
{/* Chart built with Chart.js 3 */}
<div className="grow">
{/* Change the height attribute to adjust the chart height */}
<LineChart data={chartData} width={389} height={128} />
</div>
</div>
);
}
export default DashboardCard01;
In our scenario , I map the array and produce the result using jsx , but I want the ignore duplicate data before the map operation. so how can I do that? here snipped code may help you :
import React, { useContext } from "react";
import { Context } from "../Context/Context";
import "./Navbar.css"
import cartImg from "../../cart.png"
import close from "../../close.svg"
export default function Navbar() {
const { cart, cartReducer } = useContext(Context)
function getCart(products) {
return products.map((product, index) =>
<div key={index} style={{ borderBottom: "1px solid #d5cfcf" }} >
<img src={product.image} style={{ width: '100%' }} alt="" />
<div className="cart-info">
<div>
<img src={close} onClick={() =>
cartReducer({ product, type: 'remove' })} style={{ width: 30, paddingLeft: 10 }} alt="" />
</div>
<div>
{product.name}
</div>
</div>
</div>
)
}
return (
<nav className="navbar navbar-light bg-light">
<a className="navbar-brand" href="#">
<img src={cartImg} style={{ maxWidth: 30 }} alt="" />
{cart.length > 0 && <span className="badge badge-warning badge-header">{cart.length}</span>}
<div className="navbar-cart" style={cart.length > 0 ? { width: 150 } : { width: 'fit-content' }}>
<h6> {cart.length == 0 ? 'Cart is Empty' : getCart(cart)}</h6>
</div>
</a>
</nav>
)
}
You can remove duplicates by:
const duplicatedData = [1,2,3,4,5,1,2,1,1,1,1,3,4,5,6,7,8,2,1,3,4]
const notDuplicatedData = [...new Set(duplicatedData)]
console.log(notDuplicatedData)
So in your case:
[...new Set(products)].map((product, index)