I was following a tutorial on youtube (https://youtu.be/3HNyXCPDQ7Q) for creating a portfolio website. I hosted the website using Netlify, 20 days later when I revisited the website, the website was just a blank screen. When I tested again on localhost, the problem was with sanity. When I connected to sanity, the screen would go blank.
Now the problem is that the regular website content is visible, but the data from sanity is not being fetched to the react app.
I have added some documents in the abouts schema via the sanity gui.
Abouts Schema:
export default {
name: "abouts",
title: "Abouts",
type: "document",
fields: [
{
name: "title",
title: "Title",
type: "string",
},
{
name: "description",
title: "Description",
type: "string",
},
{
name: "imgUrl",
title: "ImgUrl",
type: "image",
options: {
hotspot: true,
},
},
],
};
About.jsx code:
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion";
import "./About.scss";
import { urlFor, client } from "../../Client";
import { AppWrapper } from "../../wrapper/";
const About = () => {
const [abouts, setAbouts] = useState([]);
const querySelector = async () => {
const query = '*[_type == "abouts"]';
const aboutsQuery = await client.fetch(query);
aboutsQuery.then((data) => setAbouts(data));
};
useEffect(() => {
querySelector();
}, []);
return (
<>
<motion.div
className="app__about-header"
whileInView={{ x: [1000, 0] }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<h1 className="head-text">
<span>About</span> Me
</h1>
</motion.div>
<motion.div
className="app__about-desc"
whileInView={{ opacity: [0, 1] }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<h3 style={{ marginBottom: 10 }}>Who I am?</h3>
<p className="p-text">
Some text here.
</p>
</motion.div>
<motion.div
style={{ marginTop: 40 }}
whileInView={{ x: [-1000, 0] }}
transition={{ duration: 1 }}
viewport={{ once: true }}
>
<h2 className="head-text">
What I <span>Love to do?</span>
</h2>
</motion.div>
<div className="app__profiles">
{abouts.map((about, index) => {
return (
<motion.div
whileInView={{ opacity: [0, 1] }}
whileHover={{ scale: 1.1 }}
transition={{ duration: 1, type: "tween" }}
className="app__profile-item"
key={index}
viewport={{ once: true }}
>
<img src={urlFor(about.imgUrl)} alt={about.title} />
<h2 className="bold-text" style={{ marginTop: 20 }}>
{about.title}
</h2>
<p className="p-text">{about.description}</p>
</motion.div>
);
})}
</div>
</>
);
};
export default AppWrapper(About, "about", "app__whitebg");
This Client.js file will connect to the sanity CMS.
Client.js code:
import SanityClient from "#sanity/client";
import imageUrlBuilder from "#sanity/image-url";
export const client = SanityClient({
projectId: "hard coded value added here",
dataset: "portfoliodataset",
apiVersion: "2022-08-11",
useCdn: true,
token: "token value here",
});
const builder = imageUrlBuilder(client);
export const urlFor = (source) => builder.image(source);
I have tried the env variable as well in client.js file.
for eg. projectId: process.env.REACT_APP_SANITY_PROJECT_ID
and I have tried the hard coded values as well. Both don't seem to work.
Note that I have also added the localhost:3000 and the website url in the CORS origin.
Please help me, I am stuck on this problem for a few days now.
I dont know whether you are struck in this or not but giving you reply in case in future if someone struck at this point they can fix it. I also faced the same issue and strucked for the time being and later I realized the issue. The issue is you can't give some random name to the dataset
export const client = SanityClient({
projectId: "hard coded value added here",
dataset: "portfoliodataset",
apiVersion: "2022-08-11",
useCdn: true,
token: "token value here",
});
Here in this dataset field you have to give the dataset name which is in sanity.json file. Hope it would help you
I was facing same issue and I was able to fix it by doing following stuff:
Installing dotenv node package
Moving .env file to frontend_react folder. (I accidentally created it under src folder)
I hope that it would help you too.
Make sure you have import the key and run the sanity client on your localhost
I had exactly the same problem, in the same tutorial. make sure first you have internet connection, then try restarting everything even with internet on.
Its basically a network issue not withstanding any other possible cause of error, but you could give it time then later refresh the react app as well as the sanity client.
Also you could try adding some more code to give the compiler something new to compile, some how the app will load sanity.
Check your sanity.json or sanity.config.js file (if you are using vite) and make sure the dataset you are using is the correct one. It has to match what you have in the client.js file
I had the same issue
Related
I don't speak English very well. Please be understanding!
First, please check my code!
export default function DriveFolder() {
const [clickFolderPk, setClickFolderPk] = useState(1);
const viewFolder = async () => {
const url = `/api/store/drive/view-folder?folderId=${clickFolderPk}`;
await get(url)
.then((res) => {
console.log(res);
setMainFolder(res.directChildrenFolders);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
viewFolder();
}, [clickFolderPk]);
return (
<div className={classes.driveFolder}>
{mainFolder.map((main, key) => (
<TreeView>
<TreeItem
onClick={() => setClickFolderPk(main.FOLDER_PK)}>
<TreeItem nodeId='10' label='OSS' />
<TreeItem nodeId='6' label='Material-UI'>
<TreeItem nodeId='7' label='src'>
<TreeItem nodeId='8' label='index.js' />
<TreeItem nodeId='9' label='tree-view.js' />
</TreeItem>
</TreeItem>
</TreeItem>
</TreeView>
))}
</div>
);
}
I edited some code to make it clear. (might misspelled)
With this code, on the first rendering, since 'clickFolderPk' value is 1, I get the right data from DB.
However, since I have subfolders within folders from 'clickFolderPk' value 1, I have to request another GET REQUEST to see my subfolders from root folders.
Here is the simple image that you can understand my situation better.
this is what I get from 'clickFolderPk' value 1.
However, when I press 'kikiki', GET request functions and render like this.
.
This is not the way I want to render things.
I want every data from DB, however they don't disappear whenever I use different GET request with different PK number.
I want them stay on the screen and get the subfolders within them.
I'm struggling with this issue for quite a time.
Your help will be really appreciated!!!!!
It's all about Nesting: Folders have sub-folders, etc and it goes on...
Note: To break things down, I will answer from a React point of view disregarding how your backend api is structured or returns data.
Basically there are two main approaches,
Approach #1:
The global state is a single source of truth for all the folders think of it like this:
const [allFolders, setAllFolders] = useState([
{
id: "1",
name: "a-1",
folders: [
{
name: "a-subfolder-1",
folders: [{ name: "a-subfolder-subfolder-1" }],
},
{ name: "subfolder-2" },
],
},
]);
The problem is that any small update requires to mutate the entire state. So I will focus more on Approach #2
Approach #2:
There is the main tree that has child components, child components can expand and have children too:
import { useEffect, useState } from "react";
import "./styles.css";
export default function DriveFolder() {
const [folders, setFolders] = useState([
{ id: "1", name: "folder-a" },
{ id: "2", name: "folder-b" },
{ id: "3", name: "folder-c" }
]);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{folders.map((folder) => {
return <Folder key={folder.id} folder={folder} />;
})}
</div>
);
}
const Folder = ({ parent = undefined, folder }) => {
const [subfolders, setSubfolders] = useState([]);
const [isOpened, setOpened] = useState(false);
const hasSubfolders = subfolders.length > 0;
useEffect(() => {
// send request to your backend to fetch sub-folders
// --------------- to ease stuff I will hard code it
// with this you can limit the example of nest you wish
const maxNestsCount = 5;
const subfolderParent = parent || folder;
const subfolder = {
id: subfolderParent.id + "-sub",
name: "subfolder-of-" + subfolderParent.name
};
const currentNestCount = subfolder.name.split("-sub").length;
setSubfolders(currentNestCount < maxNestsCount ? [subfolder] : []);
// -----------------------------
}, []);
const handleToggleShowSubFolders = (e) => {
e.stopPropagation();
if (!hasSubfolders) {
return;
}
setOpened(!isOpened);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
paddingHorizontal: 5,
marginTop: 10,
marginLeft: parent ? 20 : 0,
backgroundColor: "#1678F230",
cursor: hasSubfolders ? "pointer" : undefined
}}
onClick={handleToggleShowSubFolders}
>
{folder.name}
<div style={{ display: isOpened ? "block" : "none" }}>
{subfolders.map((subfolder) => (
<Folder key={subfolder.id} parent={folder} folder={subfolder} />
))}
</div>
</div>
);
};
Try it out:
Here is the output of the sample code above:
If I make this call but the pokemon I've entered doesn't have a second type I get this error message:
Is it possible to make an if statement within the useState hook that I've named setPokemon?
If so, how can I do that or how can I get through this?
import Axios from "axios";
import React, { useState } from "react";
import "./SearchPokemon.css";
function PK() {
const api = Axios.create({
baseURL: "https://pokeapi.co/api/v2/",
});
const [pokemon, setPokemon] = useState({});
const [pokemonDescription, fetchDescription] = useState({});
const [evolution, pokemonEvolution] = useState({});
const searchPokemon = () => {
api.get(`pokemon/charmander`).then((response) => {
setPokemon({
name: response.data.name,
height: response.data.height,
weight: response.data.weight,
img: response.data.sprites.front_default,
id: response.data.id,
type: response.data.types[0].type.name,
type2: response.data.types[1].type.name,
});
api.get(`pokemon-species/${response.data.id}/`).then((response) => {
fetchDescription({
entry: response.data.flavor_text_entries[0].flavor_text,
evolution: response.data.evolution_chain.url,
});
api.get(`${response.data.evolution_chain.url}`).then((response) => {
pokemonEvolution({
evolution: response.data.chain.evolves_to[0].species.name,
});
});
});
});
};
return (
<div>
<div className="main">
<h1 style={{ textTransform: "capitalize" }}>{pokemon.name}</h1>
<h1>No. {pokemon.id}</h1>
<img src={pokemon.img} alt="" />
</div>
<div className="info">
<h3 style={{ textTransform: "capitalize" }}>
Type: {pokemon.type} {pokemon.type2}
</h3>
<h3>Height: {pokemon.height * 10} Cm</h3>
<h3>Weight: {pokemon.weight / 10} Kg</h3>
</div>
<div className="desc">
<div className="desc-info">
<h3 style={{ textTransform: "capitalize" }}>
{pokemonDescription.entry}
</h3>
</div>
</div>
<h1 style={{ textTransform: "capitalize" }}>
Evolution: {evolution.evolution}
</h1>
<button onClick={searchPokemon}>Click me</button>
</div>
);
}
export default PK;
If we first look at your error, the index 1 of your types array from your api response data is not defined. Therefore, when you try to access, it throws.
When you are not certain of the response of your api, you can use a combination of optional chaining and setting default values for that property.
This way, your code won’t break.
In your example, I believe you could do something like:
const response = {
data: {
types: []
}
};
console.log(response.data.types[1]?.type.name ?? "pick a value or leave an empty string");
// type2: response.data.types[1]?.type.name ?? ""
Notice the question mark I’ve added right after the index 1 of your expected types array. This symbol allows for optional chaining.
Then we use Nullish coalescing operator (??).
I am using react-quill 1.3.3. I also allow for the addition of images into the posts. The way that quill works right now, is that it just converts the image to a string and sends it all as text. The problem is that this seems to take a lot of extra memory (data). For instance, I am getting 413 errors, "413 (Payload Too Large)", for images that are only 20K, etc. The real way to do this is to convert your images into links and save the images to a cloud service. I already have the cloud service working for saving avatar images, so that can just be copied. The problem is, how do we get react-quill to do this? Quill seems like a one stop shop that just converts your images to text, so we are going to have to do some special coding and I have no idea how to do that.
Here is the quill component:
import React from 'react'
export default class PostEditor extends React.Component {
constructor(props) {
super(props)
this.state = { editorHtml: '', theme: 'snow' }
this.handleChange = this.handleChange.bind(this)
if (typeof window !== 'undefined') {
this.ReactQuill = require('react-quill');
require('katex');
require('react-quill/dist/quill.snow.css');
}
}
handleChange (value) {
this.props.onChange(value);
}
render() {
const ReactQuill = this.ReactQuill
const { value } = this.props;
if (typeof window !== 'undefined' && ReactQuill) {
return (
<ReactQuill
onChange={this.handleChange}
theme="snow"
value={value}
modules={PostEditor.modules}
/>
)
} else {
return <textarea />;
}
}
}
PostEditor.modules = {
toolbar: [
[{ 'header': '1'}, {'header': '2'}, { 'font': [] }],
[{size: []}],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'},
{'indent': '-1'}, {'indent': '+1'}],
['link', 'image', 'video','formula'],
['clean']
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
}
}
PostEditor.formats = [
'header', 'font', 'size',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image', 'video', 'formula'
]
// PostEditor.propTypes = {
// placeholder: PropTypes.string,
// }
Here is some work done to do something similar, but I have no idea how to add this code to my app.
What I am expecting is that we should be able to use the "add image" button on the quill toolbar and the wysiwyg works fine and the image is taken from the user's hard drive and placed in the text properly formated. The difference is that when you save, the image is sent to your cloud storage and a link is pasted into the saved html. When you view the post, that link is expanded into an image again.
If anyone knows why the images are being converted into such large sizes (more than 20MB) please let me know because I might also just accept that as a solution.
Here is the code for the part of the component that contains the quill:
{this.isActiveField('postText') && (
<Fieldset className="mt2">
<PostEditor
id="postText"
onChange={this.onChange}
value={postText}
/>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={!!ErrorHandling.getFieldErrors(errors, 'postText')}
autoHideDuration={4000}
onClose={this.handleClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{ErrorHandling.getFieldErrors(errors, 'postText')}</span>}
action={[
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={this.handleClose}
>
<CloseIcon />
</IconButton>,
]}
/>
</Fieldset>
)}
Edit:
I worked out the second solution. In my Apollo server code I added the bodyParser as recommended here. It seems to work fine now.
i have been trying to understand stripe for quite a while now. the major problem i am having is that i am a front end developer (with about a year of experience) and while i have some node.js/backend experience it is simply not enough to handle server processing of payments. i am going for the JAMstack serverless function approach using netlify. and thus far everything seems to be working out EXCEPT right at redirect to checkout i am getting the error "stripe.redirectToCheckout is not a function"
here is some of my code :
const inventory = require('./data/products.json');
exports.handler = async (event) => {
const { sku, quantity } = JSON.parse(event.body);
const product = inventory.find((p) => p.sku === sku);
const validatedQuantity = quantity > 0 && quantity < 2 ? quantity : 1;
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'auto',
shipping_address_collection: {
allowed_countries: ['US'],
},
success_url: `${process.env.URL}/success`,
cancel_url: process.env.URL,
line_items: [
{
name: 'bitch',
currency:'USD',
amount: 299,
quantity: validatedQuantity,
},
],
});
return {
statusCode: 200,
body: JSON.stringify({
sessionId: session.id,
publishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
}),
};
};
^^^^this is where i create the checkout through a serverless function
although it took some time i have been able to create a lambda function through netlify, hide my public and private keys, create a stripe element, but i am just so confused as to why i am getting this error...
blow is where the error seems to be
//client sides
import Stripe from 'stripe'
export async function handleFormSubmission(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
sku: form.get('sku'),
quantity: Number(form.get('quantity')),
};
const response = await fetch('/.netlify/functions/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
}).then((res) => res.json());
const stripe=Stripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
if(err){
console.log(err)
}
}
also if it is any help here is where i am calling the function (handleformsubmit or whatever)
import React from 'react'
import {loadStripe} from "#stripe/stripe-js"
import './Checkout.css'
import {useState} from 'react'
import {Elements,
CardElement,
useStripe,
useElements} from '#stripe/react-stripe-js'
import axios from 'axios'
import {loadProducts} from './load-products'
import {handleFormSubmission} from './stripe-purchase'
const stripePromise=loadStripe(`${process.env.STRIPE_}`)
const CheckoutForm=()=>{
const stripe=useStripe()
const elements=useElements()
const handleSubmit=async(e)=>{
e.preventDefault()
if (!stripe || !elements) {
// Stripe.js has not loaded yet. Make sure to disable
// form submission until Stripe.js has loaded.
return;
}
// Get a reference to a mounted CardElement. Elements knows how
// to find your CardElement because there can only ever be one of
// each type of element.
const cardElement = elements.getElement(CardElement);
const {error, paymentMethod}=await stripe.createPaymentMethod({
type:'card',
card:cardElement
})
loadProducts();
}
return (
<form onSubmit={handleFormSubmission} method='POST'>
<img className='checkoutImage' src='./logo2.png' />
<label class="ml-2 font-bold text-blue-700 text-md">
Enter card details below:
</label>
<fieldset className="my-2 FormGroup">
<div className="FormRow">
<CardElement options={{
style: {
base: {
fontSmoothing: 'antialiased',
fontWeight: 900,
iconColor: '#60A5FA',
fontSize: '30px',
color: '#374151',
'::placeholder': {
color: '#3182ce',
},
},
invalid: {
iconColor: '#EF4444',
color: '#DC2626',
},
complete:{
iconColor:'green',
color: 'green',
}
},
}}/>
</div>
</fieldset>
<div className='checkoutbuttonContainer'>
<button type="submit" className="scoreButtons scoreButtonGrey flex justify-center rounded-md border border-gray-300 bg-pink-600 shadow-sm px-4 py-2 bg-white text-base font-medium text-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 hover:bg-pink-500 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Submit Payment <span className='priceLine'></span> <span className='price'> </span>
</button>
</div>
</form>
)
}
const StripeSetup = () => {
return (
<div><Elements stripe={stripePromise}><CheckoutForm /></Elements></div>
)
}
export default StripeSetup
this is the first time in a while i have felt really overwhelmed by new material. im not even sure if i'm learning at a proper pace anymore nor even learning as much as just copying in code i find online lol... but this did take a good amount of work on my end nonetheless. would really love if someone could help. BTW i am now realizing that i created stripe elements etc which i don't even know if are necessary when using redirect to checkout as this is supposed to lead the client to a stripe checkout? can someone clarify all this for me. and please help if they can! thanks so much in advance
ONE last thing i wanted to say. i do not need a cart, products listing or anything. this is a one time payment for 2.99 and it will lead to the next page is the user submits the payment. not sure if that changes anything but figured the more details the better
Problem was in AWFUL documentation as well as netlify's instructions.
i needed to call loadstripe again first:
const stripe=await loadStripe(response.publishableKey);
const {err}=await stripe.redirectToCheckout({
sessionId:response.sessionId
})
I am trying to pass one state value that is imagesArray to another state that is tabData, but it is coming as undefined, PFB the code, please tell what i am doing wrong here?
constructor(props) {
super(props);
this.state = {
imagesArray: [
{
default: '/images/volImage1.png',
active: 'images/volImage1.png'
},
{
default: '/images/volImage2.png',
active: 'images/volImage2-Active.png'
},
{
default: '/images/volImage3.png',
active: 'images/volImage3.png'
},
{
default: '/images/volImage4.png',
active: 'images/volImage4.png'
},
{
default: '/images/volImage5678.png',
active: 'images/volImage5678.png'
},
],
tabData: [
{
title: 'Knowledge and experience',
content: <VolunteerTabContent1 imagesArray={this.state.imagesArray} />
//Here I am passing above imagesArray state, and this is coming as undefined and throwing error
},
{
title: 'Practical and hands on',
content: 'Tab 2 Content'
},
{
title: 'Management and leadership',
content: 'Tab 3 Content'
},
]
}
}
You cannot use this.state when setting the state itself. This won't work at all.
In your case, if imagesArray is not going to be changed during the execution and it's only some data you need, maybe you don't need to set it as part of the component's state.
You could define imagesArray as a constant outside the class or something similar, and just reference it when setting the tabData.
EDIT:
Even more. If tabData is just data you will need afterwards but it's not going to change, you don't need to set that as state either.
EDIT 2:
If this two arrays really need to be in the state, a way to achieve the desired results would be to define only the component name in the 'content' property of each tabData item, and then use that to instantiate it in the render method:
tabData: [
{
title: 'Knowledge and experience',
content: VolunteerTabContent1
},
...
and then in the render method you can do:
// Let's suppose you want the first one as an example. Do this as you need.
const item = this.state.tabData[0];
render() {
<item.content imagesArray={this.state.imagesArray} />
}
This way you'll correctly get the imagesArray form the state. JSX will get item.content's value (the VolunteerTabContent1 component in this case) and render it.
I will show in functional component it will useful for someone.
import "./styles.css";
import image1 from '../image/man.png';
import image2 from '../image/woman.png';
import React,{useState} from 'react';`enter code here`
import Avatar from '#mui/material/Avatar';
export default function App() {
const [choose,setChoose] =useState("")
const [avatar,setAvatar] = useState(image);
return (
<div className="App">
<Avatar
alt="D S"
src={avatar}
sx={{ width: 44, height: 44 }}
/>
<div>
<Avatar className='Avatars' onClick={()=> setChoose(image1)} alt="Guest"
src={image1} sx={{ width: 30, height: 30 }}/>
<label>Guest</label>
</div>
<div>
<Avatar className='Avatars' onClick={()=> setChoose(image2)} alt="Other"
src={image2} sx={{ width: 30, height: 30 }}/>
<label>Other</label>
</div>
<div>
<button className="avatar_btn1" onClick={()=>setAvatar(choose)} >OK</button>
</div>
</div>
);
}
from my code first you can choose a avatar it will not change in frontend when you click ok button it will show avatar changed one.