On my Localhost:3000, when I am running the code, I am getting an error saying:
Unhandled Runtime Error
TypeError: Cannot read properties of undefined (reading 'id')
For the Source part, it is showing a certain section of my code, which is:-
;(async () => {
20 | setSelectedMarketNft(
> 21 | listings.find((marketNft) => marketNft.asset?.id === selectedNft.id)
| ^
22 | )
23 | })()
24 | }, [selectedNft, listings, isListed])
There are certain questions of the similar type on stackOverflow, but I am unable to find any answers from any of them. I am making a web3 project, where I am using next.js, sanity and thirweb.
The source code that contains this is:-
const MakeOffer = ({ isListed, selectedNft, listings, marketPlaceModule }) => {
const [selectedMarketNft, setSelectedMarketNft] = useState()
const [enableButton, setEnableButton] = useState(false)
useEffect(() => {
if (!listings || isListed === 'false') return
;(async () => {
setSelectedMarketNft(
listings.find((marketNft) => marketNft.asset?.id === selectedNft.id)
)
})()
}, [selectedNft, listings, isListed])
useEffect(() => {
if (!selectedMarketNft || !selectedNft) return
setEnableButton(true)
}, [selectedMarketNft, selectedNft])
Link to the full source code of the file where this error is occuring is:-
https://github.com/hemang-h/Standard-Demo-Marketplace-and-Minting/blob/main/components/nft/Purchase.js
Can anyone guide me what I am doing wrong here?
EXTRAS
Just for a better understanding, this is the full source code of the file where I am facing this error:
import { useEffect, useState } from 'react'
import { HiTag } from 'react-icons/hi'
import { IoMdWallet } from 'react-icons/io'
import toast, { Toaster } from 'react-hot-toast'
const style = {
button: `mr-8 flex items-center py-2 px-12 rounded-lg cursor-pointer`,
buttonIcon: `text-xl`,
buttonText: `ml-2 text-lg font-semibold`,
}
const MakeOffer = ({ isListed, selectedNft, listings, marketPlaceModule }) => {
const [selectedMarketNft, setSelectedMarketNft] = useState()
const [enableButton, setEnableButton] = useState(false)
useEffect(() => {
if (!listings || isListed === 'false') return
;(async () => {
setSelectedMarketNft(
listings.find((marketNft) => marketNft.asset?.id === selectedNft.id)
)
})()
}, [selectedNft, listings, isListed])
useEffect(() => {
if (!selectedMarketNft || !selectedNft) return
setEnableButton(true)
}, [selectedMarketNft, selectedNft])
const confirmPurchase = (toastHandler = toast) =>
toastHandler.success(`Purchase successful!`, {
style: {
background: '#04111d',
color: '#fff',
},
})
const buyItem = async (
listingId = selectedMarketNft.id,
quantityDesired = 1,
module = marketPlaceModule
) => {
console.log(listingId, quantityDesired, module, 'david')
// yo RAZA lets goooo!!!
//yo Qazi, ok
// sure okay about to run it...
// just clicked buy now...
// still error
// where can i see the contract address of the marketplace module
// in [nftId.js]
await module
.buyoutDirectListing({
listingId: listingId,
quantityDesired: quantityDesired,
})
.catch((error) => console.error(error))
confirmPurchase()
}
return (
<div className="flex h-20 w-full items-center rounded-lg border border-[#151c22] bg-[#303339] px-12">
<Toaster position="bottom-left" reverseOrder={false} />
{isListed === 'true' ? (
<>
<div
onClick={() => {
enableButton ? buyItem(selectedMarketNft.id, 1) : null
}}
className={`${style.button} bg-[#2081e2] hover:bg-[#42a0ff]`}
>
<IoMdWallet className={style.buttonIcon} />
<div className={style.buttonText}>Buy Now</div>
</div>
<div
className={`${style.button} border border-[#151c22] bg-[#363840] hover:bg-[#4c505c]`}
>
<HiTag className={style.buttonIcon} />
<div className={style.buttonText}>Make Offer</div>
</div>
</>
) : (
<div className={`${style.button} bg-[#2081e2] hover:bg-[#42a0ff]`}>
<IoMdWallet className={style.buttonIcon} />
<div className={style.buttonText}>List Item</div>
</div>
)}
</div>
)
}
export default MakeOffer
selectedNFT is undefined as mentioned above.
The issue could be that it is running your code before it's defined.
This may go in your return function:
{(typeof selectedNFT != 'undefined') ? (
<div>Code that fetches from selectedNFT goes here.</div>
): (
<div>This could be a loading bar..</div>
)}
This will run your code ONLY after everything else executes.
You can check by console seletedNft inside makeoffer component to make sure you are getting props correctly and also check if its return object which has key with name "id" . Its cannot read id property in your selectedNft make sure its exist . I hope its help.
The error is solved, turns out that I just had to add
if(!selectedNft) return;
to successfully run my code. Final code will be as:
import { useEffect, useState } from 'react'
import { HiTag } from 'react-icons/hi'
import { IoMdWallet } from 'react-icons/io'
import toast, { Toaster } from 'react-hot-toast'
const style = {
button: `mr-8 flex items-center py-2 px-12 rounded-lg cursor-pointer`,
buttonIcon: `text-xl`,
buttonText: `ml-2 text-lg font-semibold`,
}
const MakeOffer = ({ isListed, selectedNft, listings, marketPlaceModule }) => {
const [selectedMarketNft, setSelectedMarketNft] = useState()
const [enableButton, setEnableButton] = useState(false)
useEffect(() => {
if(!selectedNft) return;
if (!listings || isListed === 'false') return
;(async () => {
setSelectedMarketNft(
listings.find((marketNft) => marketNft.asset?.id === selectedNft.id)
)
})()
}, [selectedNft, listings, isListed])
useEffect(() => {
if (!selectedMarketNft || !selectedNft) return
setEnableButton(true)
}, [selectedMarketNft, selectedNft])
const confirmPurchase = (toastHandler = toast) =>
toastHandler.success(`Purchase successful!`, {
style: {
background: '#04111d',
color: '#fff',
},
})
const buyItem = async (
listingId = selectedMarketNft.id,
quantityDesired = 1,
module = marketPlaceModule
) => {
console.log(listingId, quantityDesired, module, 'david')
// yo RAZA lets goooo!!!
//yo Qazi, ok
// sure okay about to run it...
// just clicked buy now...
// still error
// where can i see the contract address of the marketplace module
// in [nftId.js]
await module
.buyoutDirectListing({
listingId: listingId,
quantityDesired: quantityDesired,
})
.catch((error) => console.error(error))
confirmPurchase()
}
return (
<div className="flex h-20 w-full items-center rounded-lg border border-[#151c22] bg-[#303339] px-12">
<Toaster position="bottom-left" reverseOrder={false} />
{isListed === 'true' ? (
<>
<div
onClick={() => {
enableButton ? buyItem(selectedMarketNft.id, 1) : null
}}
className={`${style.button} bg-[#2081e2] hover:bg-[#42a0ff]`}
>
<IoMdWallet className={style.buttonIcon} />
<div className={style.buttonText}>Buy Now</div>
</div>
<div
className={`${style.button} border border-[#151c22] bg-[#363840] hover:bg-[#4c505c]`}
>
<HiTag className={style.buttonIcon} />
<div className={style.buttonText}>Make Offer</div>
</div>
</>
) : (
<div className={`${style.button} bg-[#2081e2] hover:bg-[#42a0ff]`}>
<IoMdWallet className={style.buttonIcon} />
<div className={style.buttonText}>List Item</div>
</div>
)}
</div>
)
}
export default MakeOffer
Related
recently the project I am working on has been upgraded to React 18. By then, suddenly a lot of issues with hydration have started to appear as warnings/errors in the console. The one I'm struggling with is "Warning: Text content did not match":
Code of this component:
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
I know that this issue results from the difference between client and server side rendering, but I don't know how to fix it and no other similar question contained solution that worked in my case.
Rest of the file, in case if the issue is not with aforementioned part:
import React, { useMemo, useState } from 'react';
import type { ReactElement } from 'react';
import AnimateHeight from 'react-animate-height';
import { BaseSelect, SelectOption } from '../molecules/base-select';
import type { FrequentlyAskedCategory } from './frequentlyAskedQuestion';
import { fromBase64 } from '../shared-services/base64Service';
interface FaqPanelProps {
mainTitle?: string;
menuTitle?: string;
showOnlyAnswer: boolean;
currentPageUrl: string;
questionsByCategories: Record<string, FrequentlyAskedCategory>,
faqStructuredDataBase64: string;
}
const FAQPanel = (props: FaqPanelProps): ReactElement => {
const categories = Object.keys(props.questionsByCategories);
const dataForSelect: Array<SelectOption> = categories.map(key => ({ label: props.questionsByCategories[key].name, value: key }));
const noOpenedElementIndex = -1;
const [openedElement, setOpenedElement] = useState<number>(-1);
const [selectedCategory, setSelectedCategory] = useState<SelectOption>(dataForSelect.length > 0 ? dataForSelect[0] : null);
const [transitionDisabled, setTransitionDisabled] = useState<boolean>(false);
const parsedStructuredData = useMemo(() => {
if(props.faqStructuredDataBase64 != null && props.faqStructuredDataBase64 !== ""){
return fromBase64(props.faqStructuredDataBase64);
}
return "";
}, [props.faqStructuredDataBase64]);
const selectNewCategory = (option: SelectOption): void => {
setTransitionDisabled(true);
setOpenedElement(noOpenedElementIndex);
setSelectedCategory(option);
}
const openElement = (index: number): void => {
if (transitionDisabled) {
setTransitionDisabled(false);
}
setOpenedElement(index === openedElement ? noOpenedElementIndex : index);
}
const speakableJson = JSON.stringify({
"#context": "https://schema.org/",
"#type": "WebPage",
"name": props.mainTitle,
"speakable":
[".O75-product-faq__headline",
".O75-product-faq__questions__item"],
"url": props.currentPageUrl
});
const hasFAQStructuredData = parsedStructuredData != null && parsedStructuredData !== "";
return (
<div className="container">
<section className="O75-product-faq" >
{
props.mainTitle
? <h2 className="O75-product-faq__headline">{props.mainTitle}</h2>
: <h4 className="O75-product-faq__categories-headline">{props.menuTitle}</h4>
}
<div className="flex">
{dataForSelect.length > 1 &&
<div className="O75-product-faq__categories">
<div className="filter__list is-hidden-sm filter">
{
dataForSelect.map((element, i) => (
<button className={`filter__btn js-filter__btn${element.value === selectedCategory?.value ? " is-active" : ""}`} key={i} onClick={() => selectNewCategory(element)}>
{element.label}
</button>))
}
</div>
<div className="filter__group is-hidden-md">
<BaseSelect selectedValue={selectedCategory}
handleChange={selectNewCategory}
options={dataForSelect} />
</div>
</div>
}
{categories.length > 0 &&
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
}
</div>
{hasFAQStructuredData && <script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html:parsedStructuredData } }></script>}
<script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html: speakableJson}} ></script>
</section>
</div>
)
}
export { FAQPanel };
export type { FaqPanelProps }
Does anyone knows how to fix it?
I am a beginner who wants to build a blog using Notion API, Next.js and Tailwind CSS. I learned the following code from here: https://egghead.io/lessons/next-js-request-notion-database-data-from-the-api-with-next-js.
The following code works fine in /post/index.js, but I get this error when I put the following code into /components/PostTest.js and import it in /index.js.
How do I solve this problem?
Error information
error screenshot
Server Error
TypeError: Cannot read properties of undefined (reading 'map')
#line 9
return posts.map((posts) => (
Source Code
import Head from "next/head";
import Link from "next/link";
import { Client } from "#notionhq/client";
import { useState } from "react";
export const PostPage = ({ posts }) => {
const [post] = useState(null);
return posts.map((posts) => (
<div className="bg-[#F5F5F7] dark:bg-black px-4 py-2 md:py-4">
<div className="bg-[#FFFFFF] dark:bg-[#141414] max-w-sm rounded-xl overflow-hidden shadow-sm container mx-auto">
<img
className="aspect-[16/9] bg-cover bg-center"
src={posts.coverImage}
alt="Post Banner"
/>
<div className="px-6 py-4">
<p className="text-[12px] md:text-[14px] dark:text-[#888888] leading-5 font-[700] pt-2 uppercase tracking-normal mb-[8px]">
{posts.Category}
</p>
<Link href={`/post/${posts.PID}`}>
<div className="text-lg md:text-xl text-[#1d1d1f] dark:text-[#F5F5F7] leading-snug font-[700]">
{posts.Title}
</div>
</Link>
<p className="text-[14px] text-[#6e6e73] dark:text-[#888888] leading-5 font-[600] pt-2">
{new Date(posts.Date).toLocaleDateString()}
</p>
</div>
</div>
</div>
));
};
export const getStaticProps = async () => {
const notion = new Client({
auth: process.env.NOTION_TOKEN,
});
// get posts more than 100 pages.
let results = [];
let data = await notion.databases.query({
database_id: process.env.NOTION_POST_DATABASE_ID,
filter: {
property: "Status",
select: {
equals: "Published",
},
},
sorts: [
{
property: "Date",
direction: "descending",
},
],
});
results = [...data.results];
while (data.has_more) {
data = await notion.databases.query({
database_id: process.env.NOTION_POST_DATABASE_ID,
filter: {
property: "Status",
select: {
equals: "Published",
},
},
start_cursor: data.next_cursor,
});
results = [...results, ...data.results];
}
const posts = results.map((post) => ({
id: post.id,
Title: post.properties.Title.title[0].text.content,
Category: post.properties.Category.select.name,
category_color: post.properties.Category.select.color,
Date: post.properties.Date.date.start,
Tags: post.properties.Tags.multi_select.map((Tags) => Tags.name),
Tags_color: post.properties.Tags.multi_select.map((TagsColor) => TagsColor.color),
PID: post.properties.PID.rich_text[0].text.content,
Author: post.properties.Author.people.map((people) => people.name),
Author_avatar_url: post.properties.Author.people.map((people) => people.avatar_url),
coverImage:
post.cover.file?.url ||
post.cover.external?.url,
}));
return {
props: {
posts,
},
revalidate: 1,
};
};
export default PostPage;
First of all, post should be inited with an empty array:
const [post, ] = useState([]);
Secondly, you cannot return an array of JSX, so wrap it in a Fragment or <>.
return (
<>
posts.map((posts) => (
...
)
</>
)
if posts is null or it's not an array, you'll get that error.
Try this fix
return (
posts?.map((posts) => (
...
)
)
If i switch between Transfer to Beneficiary and All Beneficiary tab the Select options get duplicated but if i change the second useEffect hook dependency to allBeneficiary.current instead of beneficiaries presently there, Select options doesnt duplicate, but the options are not rendered on the first render until I switch to All Beneficiary tab and back to Transfer Beneficiary
Below is the Transfer to Beneficiary code
// Hooks and Contexts
import React, { useState, useContext, useEffect, useRef } from "react";
import { TransferPointsContext } from "../../../../../context/TransferPoints";
import { LoaderContext } from "../../../../../context/Loading";
// Components
import TransferSummary from "../../../../common/modals/TransferSummary";
import Loading from "../../../../features/Loader/Loading";
// UI
import swal from "sweetalert";
import toastr from "toastr";
import Select from "react-select";
import "./css/transfer-points.css";
import { nanoid }from 'nanoid'
function TransferPoints() {
const [showTransferSummary, setShowTransferSummary] = useState(false);
const [transferSummaryData, setTransferSummaryData] = useState(false);
const [showBeneficiaryDataPage, setShowBeneficiaryDataPage] = useState(false);
const [checked, setChecked] = useState(false);
const [ options, setOptions ] = useState([])
const {
verifyCardNumber,
verifyCardState,
getBeneficiaryList,
beneficiaries, //list of beneficiaries from API
hideBeneficiaryDataPage,
setInputs,
inputs,
} = useContext(TransferPointsContext);
const { loading } = useContext(LoaderContext);
toastr.options.progressBar = true;
toastr.options = {
toastClass: "alert",
iconClasses: {
error: "alert-error",
info: "alert-info",
success: "alert-success",
warning: "alert-warning",
},
};
const allBeneficiaries = useRef([]);
useEffect(() => {
getBeneficiaryList();
}, [allBeneficiaries]);
useEffect(() => {
if (beneficiaries.data !== null) {
if (
beneficiaries.data.status === 0 &&
beneficiaries.data.success === false
) {
toastr.error("Failed to fetch user beneficiaries!", "error", {
iconClass: "toast-error",
});
console.log("beneficiaries", beneficiaries.data);
} else if (
beneficiaries.data.status === 1 &&
beneficiaries.data.success === true
) {
console.log('All beneficiary ', allBeneficiaries.current)
beneficiaries.data.data.forEach((beneficiary) => {
console.log('For each ', beneficiary)
allBeneficiaries.current.unshift({
value: beneficiary.membership_number,
label: `${beneficiary.first_name} ${beneficiary.last_name == null ? '' : beneficiary.last_name}`,
});
});
console.log('LENGTH ', allBeneficiaries.current.length)
}
}
}, [beneficiaries]);
useEffect(() => {
if (hideBeneficiaryDataPage) {
setShowBeneficiaryDataPage(false);
}
}, [hideBeneficiaryDataPage]);
const beneficiaryData = useRef({});
useEffect(() => {
if (verifyCardState.data !== null) {
if (
verifyCardState.data.status === 1 &&
verifyCardState.data.success === true
) {
setShowBeneficiaryDataPage(true);
beneficiaryData.current = {
name: `${verifyCardState.data.data.first_name} ${verifyCardState.data.data.last_name == null ? '' : verifyCardState.data.data.last_name}`,
};
toastr.success("Membership Id Validated!", "Success", {
iconClass: "toast-success",
});
return;
}
if (
verifyCardState.data.status === 0 &&
verifyCardState.data.success === false
) {
if (verifyCardState.data.message && !verifyCardState.data.data) {
toastr.error(verifyCardState.data.message, "Validation failed!", {
iconClass: "toast-error",
});
setShowBeneficiaryDataPage(false);
return;
}
setShowBeneficiaryDataPage(false);
const errorMessages = verifyCardState.data.data;
for (const error in errorMessages) {
toastr.error(errorMessages[error], "Validation Error!", {
iconClass: "toast-error",
});
}
return;
}
}
}, [verifyCardState]);
const handleSearchInput = (event) => {
const card_number = event.value;
setInputs((inputs) => ({
...inputs,
card_number,
}));
verifyCardNumber(card_number);
};
const proceedToTransfer = () => {
const amount = document.getElementById("amount").value;
if (amount.trim().length === 0) {
swal({
title: "Oops!",
text: `Amount field cannot be empty`,
icon: "error",
button: "Ok",
});
return;
}
setShowTransferSummary(!showTransferSummary);
setTransferSummaryData({
amount,
name: beneficiaryData.current.name,
membership_id: inputs.card_number,
save_beneficiary: (beneficiaryData.current.save_beneficiary == 1) ? 1 : 0,
});
};
const handleInputChange = (event) => {
event.persist();
setInputs((inputs) => ({
...inputs,
[event.target.name]: event.target.value,
}));
};
const handleChange = (event) => {
event.persist();
setChecked(event.target.checked);
const save_beneficiary = event.target.checked === true ? 1 : 0;
beneficiaryData.current.save_beneficiary = save_beneficiary;
};
console.log('LENGTH ', allBeneficiaries.current)
// console.log('current beneficiaries ', (allBeneficiaries.current) )
// console.log('CHECKED ', beneficiaryData.current.save_beneficiary )
// let id = nanoid()
return (
<div>
{/* {loading ? <Loading /> : ""} */}
<form action="#">
<div className="row">
<div className="col-md-12">
<div className="form-group">
<label htmlFor="acc-email">Select Beneficiary </label>
<Select
onChange={handleSearchInput}
className="basic-single"
classNamePrefix="select"
isClearable="true"
isSearchable="true"
name="beneficiary_card_number"
defaultValue="Select"
options={allBeneficiaries.current} //THIS RETURNS DUPLICATED VALUE ON NAVIGATING TO ALL BENEFICIARIES AND BACK
/>
</div>
<h6 class="mt-3 heading-border border-0">OR</h6>
<div className="row align-items-center justify-content-between">
<div className="col-md-8">
<label htmlFor="card_number">Enter Membership Id</label>
<input
type="text"
className="form-control"
name="card_number"
onChange={handleInputChange}
value={inputs.card_number}
/>
</div>
<div className=" col-4 " style={{marginTop: '30px', paddingLeft: '10px', textAlign: 'end',}}>
<button
onClick={() => verifyCardNumber(inputs.card_number)}
type="button"
className="btn-lg btn btn-primary"
>
Validate Id
</button>
</div>
</div>
</div>
{showBeneficiaryDataPage === true ? (
<div className="col-sm-12">
<h6 class="mt-3 heading-border border-0"></h6>
<div className="row">
<div className="col-md-6">
<div className="form-group">
<label htmlFor="acc-name">Name</label>
<input
type="text"
className="form-control"
id="acc-name"
required
disabled
name="acc-name"
value={beneficiaryData.current.name}
/>
</div>
</div>
<div className="col-md-6">
<div className="form-group">
<label htmlFor="acc-lastname">Membership Id</label>
<input
type="text"
className="form-control"
id="acc-lastname"
required
disabled
name="acc-lastname"
value={inputs.card_number}
/>
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className="form-group">
<label htmlFor="acc-lastname">Amount</label>
<input
type="text"
className="form-control"
id="amount"
required
name="amount"
onChange={handleInputChange}
value={inputs.amount}
/>
</div>
</div>
</div>
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
checked={checked}
name="save_beneficiary"
onChange={handleChange}
/>
<span className="ml-3 font-weight-bold terms-condition">
Save Beneficiary
</span>
</div>
<div className="mb-2"></div>
<div className="form-footer">
<div className="col-md-12 d-flex justify-content-center">
<button
onClick={() => proceedToTransfer()}
type="button"
className="btn-lg w-50 btn btn-primary"
>
Proceed
</button>
</div>
</div>
</div>
) : (
""
)}
</div>
</form>
{showTransferSummary === true ? (
<TransferSummary data={transferSummaryData} setShowTransferSummary={setShowTransferSummary} setShowBeneficiaryDataPage={setShowBeneficiaryDataPage}/>
) : (
""
)}
</div>
);
}
export default React.memo(TransferPoints);
This is the code for All Beneficiaries
import React, { useState, useContext, useEffect, useRef } from "react";
import "./css/transfer-points.css";
import { TransferPointsContext } from "../../../../../context/TransferPoints";
import Loading from "../../../../features/Loader/Loading";
import swal from "sweetalert";
import { LoaderContext } from "../../../../../context/Loading";
import toastr from "toastr";
import axios from 'axios'
import { ToastContainer, toast } from 'react-toastify';
function ShowAllBeneficiariesPage() {
const [ data, setData ] = useState([])
const {
getBeneficiaryList,
beneficiaries,
removeBeneficiary,
state,
} = useContext(TransferPointsContext);
const { loading } = useContext(LoaderContext);
toastr.options.progressBar = true;
toastr.options = {
toastClass: "alert",
iconClasses: {
error: "alert-error",
info: "alert-info",
success: "alert-success",
warning: "alert-warning",
},
};
const handleDelete = (id) => {
swal({
title: "Are you sure you want to remove beneficiary?",
text: "You won't be able to revert this!",
icon: "warning",
buttons: ["Cancel", "Proceed!"],
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
}).then((result) => {
if (result === true) {
removeBeneficiary(id);
console.log('ID OF RECEIVER ', id)
const newBeneficiary = data.filter(add => add.id !== id)
setData(newBeneficiary)
toastr.success("Beneficiary Removed !", "Success", {
iconClass: "toast-success",
});
}
});
};
const fetchData = () => {
axios.get(`user/beneficiaries`)
.then( res => setData(res.data.data ))
}
useEffect(() => {
fetchData()
}, [])
return (
<div>
{/* {loading ? <Loading /> : ""} */}
{ data.length === 0 ?
(
<div style={{textAlign: 'center'}}>No beneficiaries found</div>
)
:
(
<div className="col-sm-12">
{data.map((item) => {
console.log('Beneficiary Data ', data)
return (
<p className="mb-1 p-4 beneficiary-list">
{item.first_name} {item.last_name} - {item.membership_number}
<i
style={{ cursor: "pointer" }}
onClick={() => handleDelete(item.id)}
class="float-right fas fa-trash"
></i>{" "}
</p>
);
})}
</div>
)}
</div>
);
}
export default (ShowAllBeneficiariesPage);
I can't completely fix your issue, because I'd need more context and time, but I've found some issues on your code.
Never ever ever, have something in your render code that has a reference to a useRef variable. When a useRef value changes, react will completely ignore it and will not update your component. Use setState for those.
It sounds like your allBeneficiaries instead of being a ref or a state it's just derived state: It looks like it's a derived value from beneficiaries. In this case, you don't need to use any hook, just declare it as a const (e.g. const allBeneficiaries = getBeneficiaries(beneficiaries)). If you have performance issues, then consider using useMemo, but it should not be needed.
Never use a useRef as a dependency value in a useEffect - Same thing, react doesn't care about ref values, so you'll have unexpected behaviour there (effects retriggering when it shouldn't, effects not triggering when they should)
Try to avoid useEffect as much as posible. It should only be used for specific cases, such as fetching something from a server or manipulating the dom. For the rest of them, it's just problematic, best avoided.
Using allBeneficiaries (or any other ref object) as a dependency for a hook won't help you at all. The ref object's identity will never change over the lifetime of a component.
If you want to run an effect/... when the value boxed within the allBeneficiaries ref changes, the dependency will need to be allBeneficiaries.current.
Beside that, there's no good reason to use a ref for allBeneficiaries. Since it affects rendering, you will want to save it as a state atom (useState).
I added subproject(collection) to project (collection).
I put one of the elements in props. But It can't be defined.
I also would like to loop and display all elements? How Should I do?
The error =>
×
TypeError: Cannot read property 'RFv6bJMG2dmiBqWNZX8O' of undefined
TypeError: Cannot read property 'map' of undefined
23 |
| ^ 24 | {project.answers.map(answer => {
25 | return (
26 | {answer.id}
https://github.com/ayush221b/MarioPlan-react-redux-firebase-app/blob/master/src/Components/projects/ProjectDetails.js
const ProjectDetails = (props) => {
const { project } = props;
if (project) {
return (
<div className="container section project-details">
<div className="card z-depth-0">
<div className="card-content">
<span className="card-title">{ project.title }</span>
<p>{ project.content }</p>
</div>
<div className="card-action gret lighten-4 grey-text">
<div>Posted by {project.userName}</div>
<div>2nd, September, 2am</div>
</div>
<-- I would to display all data of sub projects-->
<div>{project.subproject['RFv6bJMG2dmiBqWNZX8O'].contents}</div>
{project.answers && project.answers.map(answer => {
return (
<div>{answer.id}
{answer.content}
</div>
)
</div>
</div>
)}
const mapStateToProps = (state, ownProps) => {
const id = ownProps.match.params.id;
const projects = state.firestore.data.projects;
const project = projects ? projects[id] : null;
const subprojects = project ? project.subprojects : "unloaded";
console.log("sub ="+JSON.stringify(subprojects));
return {
project: project
}
}
export default compose(
connect(mapStateToProps),
firestoreConnect(props => [
{collection: 'projects'},
{
collection: 'projects',
doc:props.match.params.id,
subcollections :[{collection:'subprojects'}] }
]
))(ProjectDetails);
Try:
<div>answers = {project.subproject?.['RFv6bJMG2dmiBqWNZX8O']?.contents}</div>
I am trying to have 3 buttons where if one is in an active state, the other 2 will automatically be inactive.
if (isActive === "true") {
setActive2("false")
setActive3("false")
}
if (isActive2 === "true") {
setActive("false")
setActive3("false")
}
if (isActive3 === "true") {
setActive("false")
setActive2("false")
}
I'm aware there's probably a better way of doing this and this is a brute force option, and I'm open to your suggestions.
I have tried putting this block of code in a function and running it whenever the buttons are clicked, but that is giving me the previous state instead of the current state.
So I was suggested to use the useEffect hook.
useEffect(() => {
if (isActive === "true") {
setActive2("false")
setActive3("false")
}
if (isActive2 === "true") {
setActive("false")
setActive3("false")
}
if (isActive3 === "true") {
setActive("false")
setActive2("false")
}
}, [isActive, isActive2, isActive3]);
However this is giving me the same issue, where the previous state is being applied.
I am for sure doing something very wrong with this hook (i have never used it before).
I have a codesandbox with all my code here
Have modified only the onChange handler in an efficient way without touching JSX much and have worked on your set-up only. CodeSandBox Link Checkbox-Selection
Some Major changes that I did are as follows:
Instead of setting seperate state to each button, I have used a single object with 3 keys isActive, isActive2 and isActive3.
const [btnStatus, setBtnStatus] = useState({
isActive: true,
isActive2: true,
isActive3: true
});
Your Handler looks something like this now.
const addPizza = (e) => {
setPizzaSize(e.target.name);
setStartPrice(parseInt(e.target.value));
const currentActive = e.target.id;
if (currentActive === "isActive") {
setBtnStatus({ isActive: true, isActive2: false, isActive3: false });
console.log("1");
}
if (currentActive === "isActive2") {
setBtnStatus({ isActive: false, isActive2: true, isActive3: false });
console.log("2");
}
if (currentActive === "isActive3") {
setBtnStatus({ isActive: false, isActive2: false, isActive3: true });
console.log("3");
}
console.log(btnStatus);
};
In your JSX each button will look like this, with own ids to track the status of button.
<button
name="Extra Large"
className={
btnStatus.isActive3
? "button btn fourth"
: "button btn fourthActive"
}
value="20"
onClick={addPizza}
id="isActive3"
>
Extra large
</button>
And here you go. All working nicely with the same code :)
I have update the code a little bit, you can create seprate constants and use them to reduce the code and also, to keep the active state use a single state only.
https://codesandbox.io/s/gracious-franklin-m8wkx?file=/src/CYO.js:0-4147
import React, { useState, useEffect } from "react";
import ButtonClickable from "./button";
import ButtonClickable2 from "./button2";
import { burgerSize, vegToppings, nonvegToppings } from "./const/size";
import "./index.css";
const CYO = () => {
const [pizzaSize, setPizzaSize] = useState("Choose your Pizza Size");
const [activeSize, setActiveSize] = useState(burgerSize.MEDIUM);
const [toppings, setToppings] = useState([]);
const [startPrice, setStartPrice] = useState(0);
const addPizza = (e) => {
setPizzaSize(e.target.name);
setStartPrice(parseInt(e.target.value));
};
const CheckSize = () => {
if (pizzaSize === "Choose your Pizza Size") {
alert("You must choose a pizza size");
} else if (toppings.length === 0) {
alert("Are you sure you don't want toppings?");
} else {
alert("Sorry, this isn't a real pizza place.");
}
};
const ToppingPlusMinus = (e) => {
const { value } = e.target;
const position = toppings.indexOf(value);
if (position !== -1) {
return removeTopping(value);
}
return addTopping(value);
};
const removeTopping = (value) => {
// We need to filter out the value from the array and return the expected new value
setToppings(toppings.filter((topping) => topping !== value));
//handleToggle();
};
const addTopping = (value) => {
setToppings([...toppings, value]);
// handleToggle();
};
let toppingPrice = toppings.length * 1.5;
let price = startPrice + toppingPrice;
return (
<div className="container CYO">
<h2 className="text-center white">Create your own pizza</h2>
<div className="row">
<div className="col-sm-8">
<div className="">
<img
src="./pizza.png"
className="img-fluid pizza"
alt="Pizza"
></img>
</div>
<h3 className="white">{pizzaSize}</h3>
<p className="white">
Your Toppings: <br />
<div className="col-lg-12">
{toppings
.filter((x) => x.name !== "")
.map((toppings) => (
<img
src={toppings}
alt="topping"
width="100px"
height="100px"
></img>
))}
</div>{" "}
</p>
</div>
<div className="col-sm-4">
<h3 className="white">Pizza size</h3>
{Object.values(burgerSize).map((value) => (
<button
name={value}
className={
activeSize !== value
? "button btn fourth"
: "button btn fourthActive"
}
value="10"
onClick={(event) => {
addPizza(event);
setActiveSize(value);
}}
>
{value}
</button>
))}
<br />
<h3 className="white">Toppings</h3>
<p className="white">Toppings are $1.50 each</p>
<div className="topping-wrapper">
<h4 className="white">Meats</h4>
{nonvegToppings.map(({ name, image }) => (
<ButtonClickable
onClick={(event) => {
ToppingPlusMinus(event);
}}
name={name}
value={image}
/>
))}
<h4 className="white">Veggies</h4>
{vegToppings.map(({ name, image }) => (
<ButtonClickable2
onClick={(event) => {
ToppingPlusMinus(event);
}}
name={name}
value={image}
/>
))}
</div>
</div>
<div className="pricefooter">
<p className="price">Price: ${price}</p>
</div>
<div className="pricefooter2">
<button className="checkout button btn fourth" onClick={CheckSize}>
Checkout
</button>
</div>
</div>
</div>
);
};
export default CYO;