I want to statically generate my navbar, so that it does not need to fetch from client side.
I am using appollo graphql and my current _app.js looks like this:
import React from 'react';
import Head from 'next/head';
import { ApolloProvider } from '#apollo/react-hooks';
import withData from '../utils/apollo';
import Header from '../components/Header';
import '../assets/css/tailwind.css';
const App = (props) => {
const { Component, pageProps, apollo } = props;
return (
<ApolloProvider client={apollo}>
<Head>
<title>...</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta charset="utf-8"></meta>
</Head>
<Header />
<Component {...pageProps} />
</ApolloProvider>
);
};
export default withData(App);
And my Header component looks like this
import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import Query from '../components/query';
import CATEGORIES_QUERY from '../apollo/queries/category/categories';
const Header = () => {
const [isExpanded, setIsExpanded] = useState(false);
const expandedClasses = isExpanded ? 'flex ' : 'hidden ';
return (
<Query query={CATEGORIES_QUERY} id={null}>
{({ data: { categories } }) => {
return (
<React.Fragment>
<nav
className={`${expandedClasses}`}
>
{categories.map((category, i) => {
return (
<Link
href="/category/[cid]"
as={`/category/${category.id}`}
passHref
key={`nav-link-${i}`}
>
<a className="text-gray-800">
{category.name}
</a>
</Link>
);
})}
</nav>
<button
className="flex"
onClick={() => {
setIsExpanded(!isExpanded);
}}
>
<span className="mb-1 bg-orange-500"></span>
<span className="mb-1 bg-orange-500"></span>
<span className="mb-1 bg-orange-500"></span>
</button>
</React.Fragment>
);
}}
</Query>
);
};
export default Header;
Next.js only allows getStaticProps on page components, I am trying to get a similar functionality on my Header Component.
I tried adding getStaticProps on header component and also on _app.js that did not work.
How can I achieve this?
This was posted recently. Also no need to ever import React itself when using next.
https://github.com/vercel/next.js/discussions/10949![enter image description here](https://i.stack.imgur.com/6hfVo.jpg)
Related
I am a novice coder trying to work through a Typescript/NextJS project that uses Auth0. It is currently set up to take users to a page (code below) that just has a login button that takes you to the Auth0 page.
This is an unnecessary step. How can I edit this to directly take the user to the Auth0 auth page upon navigating to the URL?
import "../styles/stripe.css";
import "../styles/globals.css";
import "../styles/App.css";
import "../styles/index.css";
import "../styles/progress.css";
import { FC, StrictMode } from "react";
import { getCLS, getFCP, getFID, getLCP, getTTFB } from "web-vitals";
import { useUser, UserProvider } from "#auth0/nextjs-auth0";
import type { AppProps } from "next/app";
import { ToastContainer } from "react-toastify";
import { useRouter } from "next/router";
import Head from "next/head";
import Header from "../views/Header";
const SignInRequired: FC = ({ children }) => {
const { user, error, isLoading } = useUser();
const router = useRouter();
if (isLoading) return <div />;
if (error) return <div>{error.message}</div>;
if (!user) {
return (
<>
<div className="container flex-col flex-center">
<h1>AppCo</h1>
<h2>Please sign in.</h2>
<button
className="card-test"
onClick={() => router.push("/api/auth/login")}
>Login</button>
<p>Questions?</p>
<p>hi#appco.com</p>
</div>
</>
);
}
return (
<>
{children}
</>
);
};
export default function App({ Component, pageProps }: AppProps) {
return (
<StrictMode>
<UserProvider>
{/* Default SEO. */}
<Head>
<title>AppCo</title>
<meta name="description" content="AppCo" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className="w-full">
{/* Max layout width. */}
<Header />
<SignInRequired>
<main className="w-full py-8">
<Component {...pageProps} />
</main>
<ToastContainer
autoClose={2000}
/>
</SignInRequired>
</div>
</UserProvider>
</StrictMode>
);
}
/**
* Use Web Vitals.
*
* #see https://nextjs.org/docs/advanced-features/measuring-performance
*/
export const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
}
};
Currently the code is saying "if not a user, return the sign in button".
Instead of returning that sign in button, you can redirect using Router assuming this is your next.js file.
You'll need to also import router:
import Router from 'next/router'
// or add push to your current line
import { useRouter, push } from "next/router";
if (!user) {
Router.push('/new-route')
}
I'm afraid I'm not a next.js expert but hope this gets the job done for you! Good luck!
I'm using React InstantSearch in Algolia and i'm trying to get it hide its "hits" component by default and only show up when its time to use and click on the searchBox.
I started my research here: https://www.algolia.com/doc/guides/building-search-ui/going-further/conditional-display/react/?client=jsx#handling-the-empty-query
I was able to use the query, but couldn't figure out how to apply it to the "hits" component.
So far I have this in my code:
import React from "react";
import Head from 'next/head'
import Header from '../components/Header'
import Hero from '../components/Hero'
import Titles from '../components/Titles'
import CustomHits from "../components/CustomHits";
import { useRouter } from 'next/router'
import Screenfull from "../components/Screenfull"
import { useRef } from "react";
import algoliasearch from "algoliasearch/lite";
import { InstantSearch, SearchBox, Hits, Configure, Pagination, RefinementList, connectStateResults, connectHits, Results } from "react-instantsearch-dom";
const searchClient = algoliasearch(
("XXXXXXXXXXXX"),
("XXXXXXXXXXXXXXX"),
);
const Hit = ({ hit }) => <p>{hit.title}</p>;
export default function Home({ninjas}) {
const Results = connectStateResults(({ searchState }) =>
searchState && searchState.query ? (
<div>Searching for query {searchState.query}</div>
) : (
<div>No query</div>
)
);
return (
<div>
<Head>
<title>Minerva</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<Hero />
<>
<InstantSearch
searchClient={searchClient}
indexName="prod_Directory">
{/* Adding Search Box */}
<SearchBox/>
{/* Adding Data */}
<Configure hitsPerPage={2} />
<RefinementList attribute="title" />
<Hits className ="bg-gray-500 z-50" hitComponent={Hit}/>
<Results />
</InstantSearch>
</>
<Titles title='SERIES'/>
<CustomHits />
</div>
)
}
You can create a boolean state for "display hits" and set it to true when the search box is focused, and false when it's "blurred".
const [showHits, setShowHits] = useState(false);
...
<SearchBox onFocus={()=>setShowHits(true)} onBlur={()=>setShowHits(false)}/>
...
{showHits ? <Hits hitComponent={Hit} /> : null}
Here's a codesandbox demonstrating this.
I'm new to React and I'm currently setup my first project using Gatsby. Essentially I'm creating a website that use an API created with Strapi. So far, I would like to load the navbar items using an API call like that: http://localhost:3001/sections, where for sections, I mean the items of the navbar.
For doing so, I have defined an Index page like that:
import React from "react"
import Layout from "../components/layout/layout"
import SEO from "../components/layout/seo"
import BGTState from "../context/bgt/bgtState"
import "../styles/css/begreentannery.css"
const IndexPage = () => {
return (
<BGTState>
<Layout>
<SEO title="Home" />
</Layout>
</BGTState>
)
}
export default IndexPage
the BGTState contains the getSections() method that is used inside Layout:
import React, { useContext, useEffect } from "react"
import PropTypes from "prop-types"
import { injectIntl } from "gatsby-plugin-intl"
import BGTContext from "../../context/bgt/bgtContext"
import { Spinner } from "react-bootstrap"
import Footer from "./footer"
import SearchState from "../../context/search/SearchState"
import Search from "../../components/search"
import NavbarMobile from "../../components/layout/navbarMobile"
import NavbarDesktop from "../../components/layout/navbarDesktop"
const Layout = ({ children, intl }) => {
const bgtContext = useContext(BGTContext)
const { loading, getSections, sections } = bgtContext
useEffect(() => {
getSections()
//eslint-disable-next-line
}, [])
return !loading ? (
<>
<NavbarMobile sections={sections} />
<NavbarDesktop sections={sections} />
<SearchState>
<Search />
<div className="container-fluid">
<div className="main">
{children}
<Footer />
</div>
</div>
</SearchState>
</>
) : (
<div className="container" style={{ height: "100vh" }}>
<div className="row h-100 justify-content-center align-items-center">
<Spinner animation="grow" />
</div>
</div>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default injectIntl(Layout)
the problem is in the code above, essentially I call useEffect hook which grab the sections from the API. So, until the sections are downloaded, I stop the code like so:
return !loading ? (
this is the getSections() method inside BGTState:
const getSections = async () => {
try {
setLoading()
const res = await axios.get(
`${process.env.API_URL}/sections?_sort=order:ASC`
)
dispatch({
type: GET_SECTIONS,
payload: res.data,
})
} catch (err) {
dispatch({
type: GET_SECTIONS,
payload: err.response.msg,
})
}
}
in the Index page all works fine, but the problem is in the CollectionsPage, infact I have this structure:
import React from "react"
import { injectIntl } from "gatsby-plugin-intl"
import Layout from "../components/layout/layout"
import SEO from "../components/layout/seo"
import BGTState from "../context/bgt/bgtState"
import CollectionState from "../context/collection/collectionState"
import Collection from "../components/collection"
const CollectionsPage = ({ intl }) => {
return (
<BGTState>
<Layout>
<SEO
lang={intl.locale}
title={`${intl.formatMessage({ id: "collections" })}`}
/>
<CollectionState>
<Collection id={1} />
</CollectionState>
</Layout>
</BGTState>
)
}
export default injectIntl(CollectionsPage)
essentially, the component <CollectionState> isn't mounting 'cause in Layout there is the async call on getSections().
So in Collection component, I have:
import React, { useContext, useEffect } from "react"
import CollectionContext from "../context/collection/collectionContext"
import { Link } from "gatsby"
const Collection = ({ id }) => {
const collectionContext = useContext(CollectionContext)
const { loading, collection, getCollection } = collectionContext
useEffect(() => {
getCollection(id)
}, [])
if (loading) return React.Fragment
return (
<div className="container">
<div className="row">
{/*
<img
src={`${process.env.API_URL}${collection.feature_media.url}`}
className="w-100 mt-2 mb-2"
alt={""}
/>*/}
<Link to="#" className="bg-caption bg-no-underline">
fall/winter 20/21
</Link>
</div>
</div>
)
}
export default Collection
which generate that error:
and of course getCollection is not called and will generate other errors in the Collection component
How can I revisit this mechanism? Essentially I have to:
Load all the sections
Load all the components
I'm currently building a React application with the following workflow:
List of application categories, the user will select one
Once a user has selected an application category, they will select a water type from a list
A list of products will then be displayed depending on the category and type selected.
They can then select a product to see the information i.e. product charts, images etc.
The problem:
Once a user selects a product, if they click the back button, the category and type props are lost.
Solution required:
I need to be able to maintain these props/state at all times, allowing them to be updated if the user goes back and changes category/type
I've included my code for reference below.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);
App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import WaterType from "./components/WaterType";
import Products from "./components/Products";
import Product from "./components/Product";
import "./App.css";
function App() {
return (
<div className="App">
<Switch>
<Route exact path="/" component={Home} />
<Route path="/waterType" component={WaterType} />
<Route path="/products/:productName" component={Product} />
<Route path="/products" component={Products} />
</Switch>
</div>
);
}
export default App;
Home.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import CategoryData from "./data/CategoryData";
class Home extends Component {
render() {
return (
<>
<h1>Categories</h1>
<ul>
{CategoryData.map((cat, i) => (
<li key={i}>
<Link
to={{
pathname: "/waterType",
name: cat.name,
}}
>
<img src={cat.imageURL} alt={cat.name} />
{cat.name}
</Link>
</li>
))}
</ul>
</>
);
}
}
export default Home;
WaterType.js
import React from "react";
import { Link } from "react-router-dom";
import WaterTypeData from "./data/WaterTypeData";
const WaterType = ({ location }) => {
const categorySelected = location.name;
return (
<>
<h1>Water Types</h1>
<p>Current category: {categorySelected}</p>
<ul>
{WaterTypeData.map((type, i) => (
<li key={i}>
<Link
to={{
pathname: "/products",
categorySelected: categorySelected,
waterType: type.name,
}}
>
{type.name} - {type.description}
</Link>
</li>
))}
</ul>
</>
);
};
export default WaterType;
Products.js
import React from "react";
import { Link } from "react-router-dom";
import ProductData from "./data/ProductData";
const Products = ({ location }) => {
const categorySelected = location.categorySelected;
const waterType = location.waterType;
const ProductsResult = ProductData.filter(x => x.categories.includes(categorySelected) && x.waterTypes.includes(waterType));
return (
<>
<h1>Products</h1>
<p>Current category: {categorySelected && categorySelected}</p>
<p>Water Type: {waterType && waterType}</p>
<div className="products">
<ul>
{ProductsResult.map((item, i) => (
<li key={i}>
<Link
to={{
pathname: '/products/' + item.slug,
name: item.name,
}}
>
{item.name}
</Link>
</li>
))}
</ul>
</div>
</>
);
};
export default Products;
Product.js
import React from "react";
const Product = ({ location }) => {
const productName = location.name;
return (
<>
<h1>{productName}</h1>
</>
);
};
export default Product;
The easiest solution that I can think of is to keep your selected choices (category and water type) in a top level context.
Something like this:
// ChoicesProvider.js
import React, { createContext, useState } from "react";
export const ChoicesContext = createContext(null);
export const ChoicesProvider = ({ children }) => {
const [choices, setChoices] = useState({
category: null,
waterType: null,
});
return (
<ChoicesContext.Provider value={{ choices, setChoices }}>
{children}
</ChoicesContext.Provider>
);
};
…and then in your entry point:
// index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { BrowserRouter as Router } from "react-router-dom";
import { ChoicesProvider } from "./context/ChoicesProvider";
ReactDOM.render(
<React.StrictMode>
<ChoicesProvider>
<Router>
<App />
</Router>
</ChoicesProvider>
</React.StrictMode>,
document.getElementById("root")
);
…and then each time you pick a category / waterType save the selected state in a context using setChoices defined in context. For example:
// Home.js
import React, { Component } from "react";
import { Link } from "react-router-dom";
import CategoryData from "./data/CategoryData";
import { ChoicesContext } from "../../context/ChoicesContext";
const Home = () => {
const { choices, setChoices } = useContext(ChoicesContext);
return (
<>
<h1>Categories</h1>
<ul>
{CategoryData.map((cat, i) => (
<li key={i}>
<Link
onClick={() => setChoices({ ...choices, category: cat.name })}
to={{
pathname: "/waterType",
name: cat.name,
}}
>
<img src={cat.imageURL} alt={cat.name} />
{cat.name}
</Link>
</li>
))}
</ul>
</>
);
};
export default Home;
Hopefully that gives you an idea.
Have a great day 👋
First of all, I am not sure if your Router setup is necessary. React is great for single page applications (SPA), this means, you don't neet new page for every single functionality. You can very comfortably build your application on single Route, changing just the components you currently need.
So the first possible solution is to build the application on single page, using three simple states
[category, setCategory] = useState(null)
[waterType, setWatertype] = useState(null)
[product, setProduct] = useState(null)
Based on this you can simply show or hide the select options
<div id="selections">
{!category && (<Categories />)}
{(category && !watertype) && (<WaterTypes />)}
{category && watertype) && (<Products />)}
</div>
category && <WaterTypes /> means, WaterTypes will be displayed only if category is true.
This if course requires your Link to be replaced with something like
<button type="button" onClick={() => setCategory(cat.name)}>{cat.name}</button>
With this approach you can then handle back and forward button to manipulate your states and URL, so the user has access to desired category with hyperlink like www.youreshop.com?category=first&watertype=second
Another approach is to add context to your app, what would allow you to share states between individual components.
I highly recommend Redux (https://redux.js.org/) for application state managemt.
In general it's a good idea to include Redux to modern React applications, and in your case it seems to be exactly what you're looking for.
Hope this helps.
Cheers
The page is not rendering correctly when I navigate using the href from Index Page. Its show props.title instead of showing the Title. Have provided the code for this. I am trying the instructions in the link - Trying instructions in this link
This is the Index Page from where we navigate
This is how the page is rendering
This is the expected behaviour
Index.js
import Layout from '../comps/MyLayout';
import Link from 'next/link;
const PostLink = props => (
<li>
<Link href={'/post?title=${props.title}'}>
<a>{props.title}</a>
</Link>
</li>
);
export default function Blog() {
return (
<Layout>
<h1>My Blog</h1>
<ul>
<PostLink title="Hello Next.js" />
<PostLink title="Learn Next.js is awesome" />
<PostLink title="Deploy apps with Zeit" />
</ul>
</Layout>
);
}
Layout.js
import Header from './Header';
const layoutStyle = {
margin: 20,
padding: 20,
border: '1px solid #DDD'
};
const Layout = props => (
<div style={layoutStyle}>
<Header />
{props.children}
</div>
);
export default Layout;
Post.js
import { useRouter } from 'next/router';
import Layout from '../comps/MyLayout';
const Page = () => {
const router = useRouter();
return (
<Layout>
<h1>{router.query.title}</h1>
<p>This is the blog post content.</p>
</Layout>
);
};
export default Page;
Change simple quote by backtick like so:
<Link href={`/post?title=${props.title}\`}>
This is template literals.