How to display dynamic text and images with sanity? - reactjs

index.js export const
getServerSideProps = async () => {
const query = '*[_type == "product"]';
const products = await client.fetch(query);
const bannerQuery = '*[_type == "banner"]';
const bannerData = await client.fetch(bannerQuery);
return {
props: {products, bannerData}
}import { urlFor } from '../lib/client';
HeroBanner.JSX
const HeroBanner = ({heroBanner}) => {
return (
<div className='hero-banner-container'>
<div>
<p className='beats-solo'> {heroBanner.smallText} </p>
<h3> {heroBanner.midText} </h3>
<h1> {heroBanner.largeText1} </h1>
{console.log(heroBanner.largeText1)}
<img src={urlFor(heroBanner.image)} alt='Trending'
className='hero-banner-image' />
Client.js
import sanityClient from '#sanity/client';
import imageUrlBuilder from '#sanity/image-url';
export const client = sanityClient({
projectId:'*********',
dataset:'production',
apiVersion:'2022-12-04',
useCdn:true,
token:process.env.NEXT_PUBLIC_SANITY_TOKEN
});
const builder = imageUrlBuilder(client);
export const urlFor = (source) => builder.image(source);
This is the code here I tried. If normal text is in the p,h1, and h3 elements it works fine. However, the code I have there now is
{heroBanner.smallText} and that's where my problem starts. The text doesn't display when I use those values. I did change the values on sanity.io through localhost to what I want and nothing displays. P.S: When I console logged the bannerData I got the values I'm now trying to display.

sanity body text doesn't get displayed, you need to convert it using block-content
npm install #sanity/block-content-to-react --save
import SanityBlockContent from "#sanity/block-content-to-react";
then wrap the text you want to display in block-content.
As an example, post.body is what I want to display, it'll go something like this SanityBlockContent blocks={post.body} projectId="" dataset="*" />

Related

Error 404 when trying to load lots of img

I'm build a side project for fun using create-react-app, in said project I'm fetching urls for <imgs>(700+). When I render all the <img> I get a lot of 404 errors, I thought this happened because I was trying to load too many so I tried rendering only 50 on the initial load but I still get a lot of 404 errors, also sometimes images stutter. I checked all the links that gave me a 404 error and they seem ok so, what's happening here?
I plan to load 50 more when the user gets to the bottom of the page using useEffect.
const Card = ({name, title, imageURL, categories, leaderSkill, passive, links}) => {
return(
<>
<img src={imageURL} title={`${name} ${title}`} alt={`${name} ${title}`} loading="lazy"/>
</>
)
}
export default Card;
import Card from './Card';
import { useEffect, useState } from 'react';
const Cards = ({ data }) => {
const [cards, setCards] = useState([]);
useEffect(()=>{
for(let i = 0; i < 25; i++){
setCards((cards)=>[...cards, data.shift()]);
}
},[])
return(
<div className="mt-[5vh] grid grid-cols-[repeat(5,_minmax(10px,_1fr))] lg:grid-cols-[repeat(10,_minmax(10px,_1fr))] gap-1">
{
cards.map(el => {
return(<Card key={el.id} name={el.name} title={el.title} imageURL={el.imageURL} categories={el.categories} leaderSkill={el.leaderSkill} passive={el.passive} links={el.links}/>);
})
}
</div>
)
}
export default Cards;

React State and Arrays - Double rendering causes elements duplication

I am developing a fullstack blockchain Nft Dapp with React, Ethers and Solidity. I have made some routes and a mint page with wallet connection and mintbutton. Under the mint section there's the personal collection, where infos about property and metadata are retrieved from contract.
That's the collection component code.
import { useEffect, useState } from "react";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Dino from "./Dino";
import { Contract, providers } from "ethers";
import { truncateAddress } from "./utils";
import { useWeb3React } from "#web3-react/core";
import { abi } from './abi';
export default function MyDinos() {
const { library, account} = useWeb3React();
const [dinosUri, setDinosUri] = useState([]);
const dinosTD = dinosUri.map((dino) => {
return (
<Dino key={dino} uriMetadata={dino} />
)
});
useEffect(() => {
if (!account) return;
if (!library) return;
const getDinosUri = async () => {
try {
const provider = await library.provider;
const web3Provider = new providers.Web3Provider(provider);
const signer = web3Provider.getSigner();
const contract = new Contract(process.env.REACT_APP_CONTRACT_ADDRESS, abi, signer);
const idArray = await contract.tokensOfWallet(account);
const idArrayFormatted = idArray.map(id => id.toNumber()).sort();
const uri = await contract.tokenURI(1);
const uriInPieces = uri.split("/");
const tmpDinos = [];
idArrayFormatted.forEach(id => {
const uriFormatted = `https://ipfs.io/ipfs/${uriInPieces[2]}/${id}`;
tmpDinos.push(uriFormatted);
//setDinosUri(prevArray => [...prevArray, uriFormatted])
});
setDinosUri(tmpDinos);
} catch (err) {
console.log(err);
}
}
getDinosUri();
return () => {
setDinosUri([]);
}
}, [library, account]);
return (
<>
{dinosUri.length > 0 &&
<div className='late-wow appear'>
<div className='svg-border-container bottom-border-light'></div>
<Container fluid className='sfondo-light py-4'>
<Container className='wow-container'>
<h2 className='wow appear mb-4 text-center'>Account: {truncateAddress(account)}</h2>
<h3 className='wow appear mb-4 text-center'>Dinos owned: {dinosUri.length} Dinos</h3>
<h4 className='wow appear mb-4 text-center'>Races won: COMING SOON</h4>
</Container>
</Container>
<div className='svg-border-container'></div>
<Container fluid className='sfondo-dark py-4'>
<Container>
<h2 className='mb-4'>My {dinosUri.length} Dinos</h2>
<Row className='my-5'>
{[...dinosTD]}
</Row>
</Container>
</Container>
</div>
}
</>
)
}
I managed to get the wanted result using a temporary variable tmpDinos to store the array of info, because if I used the commented method below //setDinosUri(prevArray => [...prevArray, uriFormatted]) on the first render I get the correct list, but if I change route and then get back to mint page, the collection is doubled. With the temp variable I cheated on the issue because it saves 2 times the same array content and it works good, but I don't think that's the correct React way to handle this issue. How can I get the previous code working? May it be a useEffect dependancy thing?
Thanks in advance for your attention.
A simple solution is to check if dinosUri is populated before setting its value.
if (dinosUri.length === 0) setDinosUri(prevArray => [...prevArray, uriFormatted])

How to download the iframe as pdf document in react?I have tried using jspdf and kendo-react-pdf but getting blank document

import { PDFExport, savePDF } from '#progress/kendo-react-pdf';
const [contentRef, setContentRef] = useState('');
const downloadCertificate = () => {
const element: any =
document.querySelector('#certificate') || document.body;
savePDF(element, { paperSize: 'A4' });
};
const onClickDownload = () => {
downloadCertificate();
};
return (
<div>
<PDFExport ref={pdfExportComponent} paperSize="A4">
<iframe
id="certificate"
title="View your certificate"
className="u-els-margin-left-3x u-els-margin-right-3x"
width="776px"
height="600px"
srcDoc={contentRef}
/>
</PDFExport>
</div>
);
Using the above set of code to generate the pdf, I am importing the PDF Export and wrapping it around the block of code i want to export as pdf. Here the srcDoc of iframe is what I exactly want to export which assigned to a useState. So after the page renders the info is stored in srcDoc and I want to export this as pdf on click of the button which is part of the return.
currently iframes are not supported as part of the PDF export: https://www.telerik.com/kendo-react-ui/components/drawing/limitations-browser-support/#toc-elements-and-styles

Match background with users current weather conditions

I am new to React, trying to learn and I have this unsolvable problem. I have developed a weather app, I'm still working on it, but at this moment I am stuck for 3 days trying to have a background image that changes depending on the users weather conditions. I have tried something using the icon, from openweather API. I used the same method to get the icon (image from my folder) to match users weather conditions.
import React from "react";
export default function Background(props) {
const codeMapping = {
"01d": "clear-sky-day",
"01n": "clear-sky-night",
"02d": "cloudy-day",
"02n": "cloudy-night",
"03d": "cloudy-day",
"03n": "cloudy-night",
"04d": "cloudy-day",
"04n": "cloudy-night",
"09d": "shower-rain-day",
"09n": "shower-rain-night",
"10d": "rain-day",
"10n": "rain-night",
"11d": "thunderstorm-day",
"11n": "thunderstorm-night",
"13d": "snow-day",
"13n": "snow-night",
"50d": "fog-day",
"50n": "fog-night",
};
let name = codeMapping[props.code];
return (
<img
className="background"
src={`background/${name}.jpg`}
alt={props.alt}
size="cover"
/>
);
}
So... in order to get "icon" of the input city by the user I have to call "<Background cod={weatherData.icon} alt={weatherData.description} />" from the function "Search" which is the function handling the submit form and running api call for input city. But the image is not showing(img1), but to have the img as a background I would call <Background> from my App function(img2), but in this case I will not have access to the real icon value from the input city. I should mention I have a folder in "src" called background and the images names match the codes name from the mapping.
Thank you in advance!
current preview of my app
how I see in other documentation I should set a background
You can pass the code from Search.js as the state.
App.js
const codeMapping = {
"01d": "clear-sky-day",
"01n": "clear-sky-night",
};
export const App = () => {
const [code, setCode] = useState(null) // <-- We'll update this from Search.js
const [backgroundImage, setBackgroundImage] = useState("")
useEffect(() => {
// Set background value based on the code
setBackgroundImage(codeMapping[`${code}`])
}, [code]); // <-- useEffect will run everytime the code changes
return (
<div style={{
height: '100px',
width: '100px',
backgroundImage: `${backgroundImage || "defaultBackgroundImage"}`
}}>
<Search setCode={setCode} />
</div>
)
}
Search.js
import { WeatherContext } from './App';
export const Search = ({ setCode }) => {
const handleClick = (apiResponse) => {
// Some API call returning the actual code value here //
setCode(apiResponse)
}
return (
<input
onClick={() => handleClick("01n")}
type="button"
value="Change city"
/>
)
}

Updating array using react hooks

I am making an application using the Upsplash API.
Upon rendering I want to display 30 images, witch works correctly.
import React, { useState, useEffect } from "react"
const ContextProvider =({ children }) =>{
const [allPhotos, setAllPhotos] = useState([])
const [cartItems, setCartItems] = useState([])
const [imageQuery, setImageQuery] = useState('')
useEffect(() => {
const url = `https://api.unsplash.com/photos?page=5&per_page=30&client_id=${process.env.REACT_APP_UNSPLASH_KEY}`
async function getPhotos() {
const photosPromise = await fetch(url)
const photos = await photosPromise.json()
setAllPhotos(photos)
}
getPhotos()
},[])
I then pass AllPhotos to my Photos.js using my context, and map over allPhotos, passing the photo to my Image component to display information about the image.
import React, {useContext} from "react"
import {Context} from "../Context"
function Photos(){
const {allPhotos} = useContext(Context)
const imageElements = allPhotos.map((photo,index) =>(
<Image key={photo.id} photo={photo}/>
))
return(
<>
<main>
{imageElements}
</main>
</>
)
}
export default Photos
const Image = ({ photo }) => {
return (
<div
<img src={photo.urls.thumb} className="image-grid" alt="" />
</div>
)
}
From here the images from the API display and everything is working correctly.
What I want to do now is add a search query, where the users can search for certain images.
I made a component for the input value
import React, { useContext } from "react"
import {Context} from "../../Context"
const QueryInput = () =>{
const {imageQuery, setImageQuery, SearchImage} = useContext(Context)
return(
<form onSubmit={SearchImage} >
<label>
Search Photos
<input
type="text"
className="query-input"
placeholder="Search Images"
value={imageQuery}
onChange={(e) => setImageQuery(e.target.value) }
/>
</label>
<button type="submit">Search Image</button>
</form>
)
}
export default QueryInput
I made a searchQuery function in my context
const SearchImage = async (e) =>{
e.preventDefault()
const queryUrl = `https://api.unsplash.com/search/photos?
age=5&per_page=30&query=${imageQuery}&client_id=${APP_KEY}`
const response = await fetch(queryUrl)
const queryPhotos = await response.json();
setAllPhotos(prevState => [...prevState, ...queryPhotos])
}
Everything works so far, I can console.log(queryPhotos) and get the users images of the query they searched for. If I search for "stars" I will get a bunch of images with stars.
What im having trouble doing is mapping through allPhotos again and displaying the query search images.
The error im having is
TypeError: queryPhotos is not iterable
I have been at this for awhile. Any information/advice would be greatly appreciated. Any questions about the code or need additional information I can provide it. THANK YOU.
In short.
queryPhotos is not an array.
unsplash api response for api /photos and /search/photos is a bit different. One return an array, while the other is an object, you need to access photos in results
So, change this line from
setAllPhotos(prevState => [...prevState, ...queryPhotos])
to
setAllPhotos(prevState => [...prevState, ...queryPhotos.results])
Should fix your problem.

Resources