Incremental static generation not working in version 13 - javascript

Does ISR works for anyone in NextJS 13 Beta version?
I am doing the following by using revalidate.
export const revalidate = 15;
When I perform npm run build, it still ends up as a SSG (static site generated) page.
The symbol is empty white.
What am I missing? I was expecting the page to be ISR.
P.S: Also tried with fetch api and { next: { revalidate: 15 }} and outcome is the same.
In terminal, this is output after npm run build.
This is not a dynamic route.
Location is app/page.jsx So this opens at localhost:3000
import axios from "axios";
import Card from "#/components/Card";
export const revalidate = 15; // seems to have no effect
const AllCards = async () => {
const url = 'http://localhost:3001/cards';
const fetchCards = await axios.get(url);
const cards = fetchCards.data.data;
return (
<main>
<div className='text-3xl font-bold underline text-center mb-4 mt-4'>
All Cards
</div>
<div className='flex flex-wrap justify-center gap-2'>
{cards.map(c => <Card vanity={c.vanity} art={c.art} id={c.id} />)}
</div>
</main>
);
}
export default AllCards;

Related

How to use Preact signals with astro to share state

Problem
When i change the tag value it only changes on the select component but not in the index.astro
I have folder signals where i export signal
export const tagSignal = signal<string>("all");
I use it like this in Select.tsx component, and here evryting changes
import { tagSignal } from "#signal/*";
const setTagValue = (value: string) => {
tagSignal.value = value;
console.log("select", tagSignal.value);
};
export const Select = () => {
const [display, setDisplay] = useState(false);
const [selectedName, setSelectedName] = useState("all"); // this will be change to only signals still under refator
setTagValue(selectedName);
-------
------
but when I import it to index.astro like this I get only "all" value witch is inital value
---
import { Icon } from "astro-icon";
import { Picture } from "astro-imagetools/components";
import Layout from "#layouts/Layout.astro";
import { Select } from "#components/Select";
import Card from "#components/Card.astro";
import { getCollection } from "astro:content";
import { getProjectsByTag } from "#utils/*";
import { tagSignal } from "#signal/*";
const projects = await getCollection("projects");
const filteredProjects = getProjectsByTag(projects, tagSignal.value);
// TODO: add links
console.log("index", tagSignal.value);
---
/// some code here
<section id="projects" class="projects">
<Select client:only="preact" />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</section>
---
I see two issues here.
You are depending on dynamic JS variables in an .astro file. It doesn't work the way you are expecting—all the javascript in .astro files, with the exception of the "islands," e.g., your Select.tsx component, is being evaluated when the page is being built. So Astro grabs the initial value of tagSignal, but makes it a static string.
People can get bitten by, e.g., the trivial ©2010—{new Date().getFullYear()} in the footer – it won't magically update on the new year's eve if used in .astro file.
The state (signal's current value) is not shared accross the islands. If you want to share it, you need either a global state solution (I haven't used it in Astro yet), or just create a common parent for the Select and the filtering logic, e.g.:
{/* the signal will be declared (or imported) in `DynamicSection`*/}
<DynamicSection client:only="preact">
<Select />
<div class="projects-wrapper">
{
filteredProjects.map(({ data: { title, heroImage } }) => (
<Card name={title} bg_path={heroImage} />
))
}
</div>
</ DynamicSection>
(The simplest global state solution would be probably using the url with a query string, and derive the state from its value).

useContext not updating on state change in React

I have managed to convert my class component into a function one allowing me to use useContext however I am a little stuck when it comes to using and getting the status of what I need when it's changed.
I have a component called input.js which sits in src/components/input.js
// input.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import Button from '../components/button';
import { getData } from '../utils/debugger';
import { DropdownContext } from '../utils/DropdownContext';
function InputForm(props) {
const [loaded, setLoaded] = useState(false);
const [dropdown, setDropdown] = useState('RTS');
const [value, setValue] = useState('');
const [data, setData] = useState([]);
function queryData(dropdown) {
return axios({
method: 'GET',
url: `http://xxx/debugger/${dropdown}`,
})
.then((res) => res.data.map((k) => k['key']['name']))
.catch((err) => console.error(err));
}
const handleChange = (event) => {
setValue(event.target.value);
};
const handleDropdown = async (event) => {
const value = event.target.value;
try {
const newData = await queryData(value);
setData(newData);
setDropdown(value);
if (newData.length > 0) {
setValue(newData[0]);
}
console.log('newData = ' + JSON.stringify(newData));
} catch (ex) {
console.error('Could not get data from axios');
}
};
const handleSubmit = (event) => {
event.preventDefault();
};
useEffect(() => {
queryData(dropdown)
.then((data) => {
setData(data);
if (data && data.length > 0) {
setValue(data[0]);
}
})
.catch(() => {
setLoaded(false);
});
}, []);
return (
<form onSubmit={handleSubmit} className='flex items-center'>
<select
value={dropdown}
onChange={handleDropdown}
className='relative w-full bg-white border border-gray-300 rounded-md shadow-sm px-1 py-3 text-center cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm mr-5'>
<DropdownContext.Provider value={dropdown}>
<option value='RTS'>RTS</option>
<option value='RTB'>RTB</option>
<option value='MPC'>MPC</option>
<option value='MPC_DSP'>MPC_DSP</option>
</DropdownContext.Provider>
</select>
<select
value={value}
onChange={handleChange}
className='relative w-full bg-white border border-gray-300 rounded-md shadow-sm px-1 py-3 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm mr-5'>
{data.map((r) => (
<option key={r} value={r}>
{r}
</option>
))}
</select>
{/* {console.log('---')}
{console.log('these will be entered into the getData()')}
{console.log(`this.state.dropdown = ${dropdown}`)}
{console.log(`this.state.value = ${value}`)} */}
<Button onClick={() => getData(dropdown, value)} color='green'>
Generate
</Button>
</form>
);
}
export default InputForm;
The component works perfectly fine outside of the context. Now I want to pass the dropdown state into App.js
// App.js
import React, {useContext } from 'react';
import './index.css';
import Button from '../src/components/button';
import RTSButtons from '../src/components/rtsButtons';
import RTBButtons from '../src/components/rtbButtons';
import MPCButtons from '../src/components/mpcButtons';
import { DropdownContext } from '../src/utils/DropdownContext';
const sectionStyles = 'mt-5 border-b pb-5';
export default function App() {
const buttonState = useContext(DropdownContext);
console.log(buttonState);
if (buttonState === 'RTS') {
console.log('RTS');
return <RTSButtons />;
} else if (buttonState === 'RTB') {
console.log('RTB');
return <RTBButtons />;
} else if (buttonState === 'MPC') {
console.log('MPC');
return <MPCButtons />;
}
return (
<div>
<section id='response' className={`flex justify-between ${sectionStyles}`}>
<div>
<Button color='red'>
<a href='http://exchange-debugger' target='_blank' rel='noreferrer'>
create a capture
</a>
</Button>
<Button onClick={() => console.log('Feedback was giving')} color='purple'>
<a
href='https://docs.google.com/forms/d/e/1FAIpQLSfzebOfAeXqGLqAp5E1l2fW1nTqSzYRwpqKG56HPXey9GQLcA/viewform'
target='_blank'
rel='noreferrer'>
feedback
</a>
</Button>
</div>
</section>
<section>{buttonState}</section>
</div>
);
}
I have a util file which is the following:
import { createContext } from 'react';
export const DropdownContext = createContext('RTS');
I now don't really know where to place my <DropdownContext.Provider value={dropdown}> To get the correct value so I'm able to pass it over to App.js. I have seen some tutorials that shows it being placed around other components...where as I just want to pass a state into another file of have that state avaiable globally.
Any help would be great, i feel I am very close but so far.
Here's a generic recipe for useContext. Say you have a file context.js:
import { createContext, useContext, useState } from 'react';
// No need to export this, we'll create custom hook around it
const SomeCtx = createContext();
// Custom hook, returns array with state, setter
export const useSomeCtx = () => useContext(SomeCtx);
// Convenience hook so you don't have to destructure
// everywhere, returns read-only state
export const useSomeCtxState = () => {
const [state] = useContext(SomeCtx);
return state;
};
// Provider, goes in render tree above components where you
// import/use the accessor hooks above.
export const SomeCtxProvider = ({children, init}) => {
const myCtx = useState(init); // [myCtxState, setMyCtxState]
return <SomeCtx.Provider value={myCtx}>{children}</SomeCtx.Provider>;
};
Then in your index.js:
import {SomeCtxProvider} from './context.js';
// other stuff
// Setting at the root makes the context globally visible, you
// may want to apply the principle of least privilege
// and put this further down in the render tree.
ReactDOM.render(<SomeCtxProvider><App /></SomeCtxProvider>, someDOMElement);
Then in your App.js
import {useSomeCtx} from './context.js';
function App() {
const [state, setState] = useSomeCtx(); // or state = useSomeCtxState()
// whatever
}
Now you can make state changes just like you normally would, and any component that uses the hooks you're providing will re-render and get the latest state. You can wire the setter to whatever event listener(s) are required (like a click on your button).
Note that unlike the older pattern where you kept your entire app state in one huge object, you aren't limited to one context. You can have different contexts with their own custom hooks per the pattern above and have them be available at any point in the render tree below where you put the provider (which in the example I gave is at the root, so everywhere in that case).
Also note that this is pretty short and sweet for how powerful it is, any component in your entire app can access this just by importing and using the custom hook defined above and will automatically re-render if it changes. You may want to be more careful about handing out the setter, which is why I included the read-only hook: global variables are evil. Although if you have an app that complex you should probably be using useReducer and dispatching actions rather than useState.
It's better to apply context API for consuming components
Take a look at your Submit action. there are 2 approaches to pursue.
onSubmit on form element
onClick on button element inside a form through type="submit"
Seems you're not sending anything after a submission using one of the above-mentioned methods. check it out.
You can also just pass the state up via a state changing function
and useState in your App.js. Then you can pass the state down to other components.
As an above picture, react never trigger the changes if you put the whole object into a new object.
Make sure you trigger the changes so that react think there are some changes and react trigger change to Context API.
I highly suggest creating a new object and adding one by one inside that new object and then finally adding to Context.
As you see in the above snap, I used two different files and get context from another file and update that context from another file.

How should I traverse through a folder of .gifs to display them in a list using Gatsby.js

I am trying to figure out how to best display a list of gifs, for starters, located locally in a directory on my machine using Gatsby.js. I did a long Gatsby tutorial that went through using gatbsy-remark and gatsby-image-remark, etc and found that those plugins did not like .gifs... Specifically, anything that required the gatsby plugin 'gatsby-plugin-sharp' and 'gatsby-transformer-sharp' did not work when trying to display gifs. GrahpQL even warns you that targeting childImageSharp on an object that is a .gif will return null and it does...
So I have this gatsby code I'm using in a navigation component...
import React from "react"
import Logo from "../images/glitch-logo.gif"
import Menu from "./menu"
import * as headerStyles from './header.module.scss'
class Navigation extends React.Component {
render() {
return (
<>
<img className={headerStyles.logo} src={Logo} alt="NFT Category Filter" onClick={() => this.toggleMenu()} onKeyDown={this.handleClick}></img>
<Menu ref={el => (this.childMenu = el)} />
</>
)
}
toggleMenu() {
this.childMenu.open()
}
}
export default Navigation
can't I just do something similar in my index file if, for example, I wanted to display an array of .gifs? Something like this?
import * as ImageList from '../images/categories' //folder with .gifs!!!
import Layout from '../components/layout'
import * as layoutStyles from '../components/layout.module.scss'
const IndexPage = () => {
const ImageList = (props) => {
const nfts = props.nfts;
const listImages = nfts.map((nft) => {
<li>{nft}</li>
}
)
}
or is there a plugin for gatsby worth downloading that makes using .gifs easier?
Your approach, assuming all the notation-related stuff (paths, naming, etc) is correct. However, your IndexPage should look like:
const IndexPage = (props) => {
const nfts = props.nfts;
return <ul>
{nfts.map((nft) => {
return <li>{nft}</li>
}}
</ul>
}
.map loops need to return an array, you were missing the statement.
The props are inherited from the top-level components, not from their inner functions. So props, should be got by IndexPage and used in the JSX loop in the return statement of the component.

ReactJS :: Jest Testing :: "TypeError: Cannot read property 'then' of undefined"

I am currently having some trouble compiling a test for an online study task to see whether the fetch() function of my weather application is working correctly.
I have made use of the useEffect() hook to fetch the data from the OpenWeather API to store and render once the API's URL changes.
I am new to Jest testing and have tried a couple of things, following tutorials and other sources, but am unfortunately not having any success. My current solution is returning the following error: "TypeError: Cannot read property 'then' of undefined"
Please see below my code:
App.js
// Imported hooks and react libraries.
import React, { useState, useEffect } from 'react';
// Imported stylesheet.
import './App.css';
// Imported components.
import Header from './components/Header';
import Footer from './components/Footer';
// Imported countries from i18n-iso-countries to get the iso code and return the country name in English.
import countries from 'i18n-iso-countries';
// Imported icons from Font Awesome.
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import {
faCloudSunRain,
faHandHoldingWater,
faHandSparkles,
faMapMarkerAlt,
faSearchLocation,
faTemperatureHigh,
faTemperatureLow,
faWind
} from '#fortawesome/free-solid-svg-icons';
countries.registerLocale(require('i18n-iso-countries/langs/en.json'));
function App() {
// Setting the initial states of the app to store the response and the locations. Using the useState hook to set the data. Showing Durban as
// an example.
const [apiData, setApiData] = useState({});
const [getState, setGetState] = useState('Durban');
const [state, setState] = useState('Durban');
// Constructing the API URL and accessing the key via the process.env variable.
const apiKey = process.env.REACT_APP_API_KEY;
const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${state}&APPID=${apiKey}`;
console.log (process.env.REACT_APP_API_KEY);
// Using the useEffect hook to fetch the data from the API to store and render once the API's URL changes.
useEffect(() => {
fetch(apiUrl)
.then((res) => res.json())
.then((data) => setApiData(data));
}, [apiUrl]);
// Constructed an input handler to get the data once requested and to store in the getState.
const inputHandler = (event) => {
setGetState(event.target.value);
};
// Constructed a submit handler to handle the request once the search button is clicked.
const submitHandler = () => {
setState(getState);
};
// Constructed a kelvin to celsius converter to output the temperature in celsius.
const kelvinToCelsius = (k) => {
return (k - 273.15).toFixed(2);
};
// Constructed a miles to kilometers converter to output the temperature in kilometers.
const milesToKilometers = (k) => {
return (k * 3.6).toFixed(2);
};
// Created a function to capitalize the first letters of each part of the countries' names.
function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
// Returning the data. Included the React Bootstrap stylesheet's link and called the "Header" and "Footer" components below. I also called the
// following from the API:
// {apiData.weather[0].icon} - The icon displaying the current conditions.
// {apiData.name} - The city's name.
// {countries.getName(apiData.sys.country, 'en', { select: 'official', })} - The country's name with the first letters capitalized.
// {kelvinToCelsius(apiData.main.temp_min)} - The minimum temperature.
// {kelvinToCelsius(apiData.main.temp_max)} - The maximum temperature.
// {kelvinToCelsius(apiData.main.feels_like)} - The "feels like" temperature, taking into account the temperatures and conditions.
// {apiData.weather[0].main} - The summarized condition.
// {capitalizeFirstLetter(apiData.weather[0].description)} - The full condition's description.
// {apiData.main.humidity} - The humidity percentage.
// {milesToKilometers(apiData.wind.speed)} - The wind speed.
// Called the inputHandler (input section) and submitHandler (button) to get the current state's values and added Font Awesome icons. Also
// added a loading message for if the page load takes a while. Currently only shows if there is no input or upon refresh.
return (
<div className="App">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css"></link>
<Header />
<div className="container">
<div className="searchsection">
<label htmlFor="location-name">Enter Location:</label>
<input
type="text"
id="location-name"
onChange={inputHandler}
value={getState}
/>
<button onClick={submitHandler}><FontAwesomeIcon icon={faSearchLocation} /></button>
</div>
<div className="mt-3 mx-auto" style={{ width: '60vw' }}>
{apiData.main ? (
<div id="weathercontainer">
<div id="mainweather">
<img
src={`http://openweathermap.org/img/wn/${apiData.weather[0].icon}#2x.png`}
alt="weather status icon"
className="weather-icon"
/>
<p className="h2">{kelvinToCelsius(apiData.main.temp)}°C</p>
<h3><FontAwesomeIcon icon={faMapMarkerAlt} /> {apiData.name}</h3>
<h3>{countries.getName(apiData.sys.country, 'en', { select: 'official', })}</h3>
</div>
<div className="temperatureconditions">
<div id="temperature">
<h5>Temperature:</h5>
<p><FontAwesomeIcon icon={faTemperatureLow} /> {kelvinToCelsius(apiData.main.temp_min)}°C</p>
<p><FontAwesomeIcon icon={faTemperatureHigh} /> {kelvinToCelsius(apiData.main.temp_max)}°C</p>
<p><FontAwesomeIcon icon={faHandSparkles} /> Feels like: {kelvinToCelsius(apiData.main.feels_like)}°C</p>
</div>
<div id="conditions">
<h5>Conditions:</h5>
<p><FontAwesomeIcon icon={faCloudSunRain} /> {apiData.weather[0].main}: {capitalizeFirstLetter(apiData.weather[0].description)}</p>
<p><FontAwesomeIcon icon={faHandHoldingWater} /> Humidity: {apiData.main.humidity}%</p>
<p><FontAwesomeIcon icon={faWind} /> Wind Speed: {milesToKilometers(apiData.wind.speed)} km/h</p>
</div>
</div>
</div>
) : (
<h1 id="loading">Weather Bot is Loading...</h1>
)}
</div>
</div>
<Footer />
</div>
);
}
// Exported App to Index.js.
export default App;
App.Fetch.React.test.js
import React from 'react';
import App from '../App';
import { render, screen, act } from '#testing-library/react';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () =>
Promise.resolve({
value: "Durban"
}),
})
);
describe("App", () => {
it("loads Durban city name", async () => {
await act(async () => render(<App />));
expect(screen.getByText("Durban")).toBeInTheDocument();
});
});
Does anyone mind helping?
I have managed to find a solution to run the test successfully.
The test's code is as follows (with notes for referencing):
/* Storing a reference to the global.fetch function so that we can use it to cleanup the mock after we're done testing. */
const unmockedFetch = global.fetch
/* Fetching the Promise with the JSON method, which also returns the Promise with the data. */
beforeAll(() => {
global.fetch = () =>
Promise.resolve({
json: () => Promise.resolve([]),
})
})
/* Using the afterAll() jest hook and calling the global.fetch function to cleanup mock test. */
afterAll(() => {
global.fetch = unmockedFetch
})
/* Adding a description of what should be executed and describing the test that will determine whether it is executed successfully or not.
Utilizing the async function due to the await keyword being used to invoke asynchronous code. Using the expect() and toHaveProperty() functions
to see whether the fetched data from JSON matches the keys stipulated. */
describe('Displaying the temperature and the wind speed', () => {
test('Is it working?', async () => {
const json = await withFetch()
expect(json).toHaveProperty(['main', 'temp']);
expect(json).toHaveProperty(['wind', 'speed']);
})
})
I hope that all is in order and that it will help someone else in future.

Fetch data at component level and have it prerendered/ssr nextjs

I'm working with Nextjs for the first time.
I'm trying to create multiple layouts which will consist on a <Header><different-layouts-for-each><Footer> structure.
The issue that I'm facing is that getStaticProps or getServerProps can run at the page level only.
Since I need SEO on the navigation I suppose I should get it's props on every single page file using one of the two mentioned methods.
The problem here is that I'd have to get the menu props on each one of the pages, but having different templates I will have to repeat myself on all of them in order to bring the content statically or prerendered and be SEO readable.
Getting the menu props on the <MainNav> component would be the ideal situation.
I tried doing an asnyc/await on the component:
<Header> component
import Logo from "../components/header/logo";
import MainNav from "../components/header/mainnav.js";
function Header() {
return (
<div className="headwrapper container mx-auto py-8 flex items-center">
<Logo />
<MainNav/>
</div>
);
}
export default Header;
<MainNav> component
import Link from "next/link";
import { WpHeader } from "../../lib/wpapi";
import React, { useEffect, useState } from "react";
function MainNav() {
const [nav, setNav] = useState(0);
useEffect(() => {
const fetchData = async () => {
const wp = new WpHeader();
const call = await wp.getAxiosMenu();
console.log(call);
setNav(call);
};
fetchData();
}, []);
return (
<div className="navigation text-right w-3/4">
<ul className="main-navigation">
{nav
? nav.map(item => (
<li key={item.id} className="inline-block mx-2">
<Link href={item.path}>
<a>{item.label}</a>
</Link>
</li>
))
: "loading"}
</ul>
</div>
);
}
export default MainNav;
The issue here is that this will return a promise and the html will show "loading" instead of the actual menu html.
Any help or article that could help me on this?
Thanks.

Categories