I am trying to update an object to the nested array
and the code below works well when I tried with React Hooks, using useState to update the state.
Then I tried to add a state management library named Recoil, a new library from Facebook, which is super simple and behaves like Hooks.
But after I changed my code adding some Recoil codes, like the atom, useRecoilState, adding a new object to the nested array stopped working.
I tried a few different methods to solve this and find other solutions but didn't go well.
Does anyone know how to solve this? You can check the code and codesandbox link below.
https://codesandbox.io/s/nested-arrays-qrw95?file=/src/App.js:0-1547
import React from "react";
import { RecoilRoot, atom, useRecoilState } from "recoil";
const todoListState = atom({
key: "TodoList",
default: [
{
name: "Lee",
device: [
{ name: "MBPR", price: 3000 },
{ name: "MBA", price: 2000 }
]
}
]
});
export function TodoApp() {
const [list, setList] = useRecoilState(todoListState);
return (
<>
{list.map((x, i) => {
return (
<div style={{ border: "1px solid black", textAlign: "left" }} key={i}>
<div>{x.name}</div>
<div>Devices</div>
<ul>
{x.device.map((y, j) => {
return (
<>
<li key={j}>{y.name}</li>
</>
);
})}
</ul>
<button
onClick={() => {
const clone = [...list];
clone[i].device = [...clone[i].device, { name: "IPX" }];
setList(clone);
}}
>
Add List
</button>
</div>
);
})}
<button
onClick={() => {
const newData = { name: "Kristoffer", device: [{ name: "Nokia" }] };
setList([...list, newData]);
}}
>
Add Item
</button>
</>
);
}
export default function App() {
return (
<RecoilRoot>
<div className="App">
<TodoApp />
</div>
</RecoilRoot>
);
}
Related
Want to make a Quiz application on WordPress Gutenberg with React? The problem goes, when I try to make a repeater field of the quiz section. I use UseState to make the components Repeat the section onClick. But I can't edit the input field after repeating it.
index.js
import { registerBlockType } from "#wordpress/blocks";
import "./style.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
attributes: {
ids: { type: "array", default: 1},
question: { type: "string", default:"Example Question"},
answers: { type: "array", default: [""] },
correctAnswer: { type: "number", default: undefined },
bgColor: { type: "string", default: "#ededed" },
},
edit: Edit,
save,
});
edit.js `
import { __ } from "#wordpress/i18n";
import {
TextControl,
Flex,
FlexBlock,
FlexItem,
Button,
Icon,
} from "#wordpress/components";
import { useBlockProps } from "#wordpress/block-editor";
const { useState} = wp.element;
import "./editor.scss";
import QuizWrapper from "./components/QuizWrapper";
import TestComponent from "./components/TestComponent";
export default function Edit(props) {
const [quizwrapper, setQuizwrapper] = useState([<QuizWrapper props={props} key={0} />])
const blockProps = useBlockProps({
className: "quiz-wrapper",
});
function addSection() {
setQuizwrapper([...quizwrapper,<QuizWrapper props={props} key={quizwrapper.length} />]);
}
return (
<div {...blockProps}>
<QuizWrapper props={props}></QuizWrapper>
{quizwrapper}
<Button isPrimary onClick={addSection}>
Add Another Section
</Button>
</div>
);
}
When I use <QuizWraper /> as a component it works fine on editing and changing on Question and Answer section, image has been uploaded below
but for the UseState hook for repeating the component {quizwrapper} repeates the section but doesn't allow me to edit and change the section
Image of the editor WordPress dashboard
QuizWrapper.jsx
import React from 'react'
import Question from "./Question"
import Answers from "./Answers"
const QuizWrapper = ({props}) => {
return (
<div className='quizes'>
<Question props={props}></Question>
<Answers props={props}></Answers>
</div>
)
}
export default QuizWrapper
Question.js
import { TextControl } from "#wordpress/components";
export default function Question({props}) {
function updateQuestion(value) {
props.setAttributes({ question: value });
}
return (
<div className="question">
<h2 style={{ fontSize: "20px", margin: "20px 0 8px 0" }}>Questions: </h2>
<TextControl
value={props.attributes.question}
onChange={updateQuestion}
/>
</div>
);
}
Answer.js
import { TextControl, Button, Icon } from "#wordpress/components";
export default function Answers({ ids, props }) {
function markasCorrect(index) {
props.setAttributes({ correctAnswer: index });
}
function deleteAnswer(indextoDelete) {
const newAnswer = props.attributes.answers.filter(function (x, index) {
return index != indextoDelete;
});
props.setAttributes({ answers: newAnswer });
if (indextoDelete == props.attributes.correctAnswer) {
props.setAttributes({ correctAnswer: undefined });
}
}
return (
<div className="answers" key={ids}>
<p style={{ fontSize: "13px", margin: "20px 0 8px 0" }}>Answers: </p>
{props.attributes.answers.map((answer, index) => {
return (
<div className="answer-wrapper">
<Button onClick={() => markasCorrect(index)}>
<Icon
className="mark-as-correct"
icon={
props.attributes.correctAnswer == index ? "yes-alt" : "yes"
}
/>
</Button>
<TextControl
value={answer}
onChange={(updateanswer) => {
const newAnswers = props.attributes.answers.concat([]);
newAnswers[index] = updateanswer;
props.setAttributes({ answers: newAnswers });
}}
></TextControl>
<Button
isLink
className="deleteBtn"
onClick={() => deleteAnswer(index)}
>
Delete
</Button>
</div>
);
})}
<Button
isPrimary
onClick={() => {
props.setAttributes({
answers: props.attributes.answers.concat([""]),
});
}}
>
Add Another Answer
</Button>
</div>
);
}
Need Solution: How I can make the repeater section of the <Quizwapper>component and how I can make the repeater section where the <Question/> and <Answer/> components will work fine.
Currently I am working on a function that exports components to PDFs
`
import { PDFExport } from "#progress/kendo-react-pdf";
import { useAppDispatch, useAppSelector } from "hooks";
import { get } from "lodash";
import { ManagerGeotechnicalDetail } from "pages/GeotechnicalDetail";
import PointDetail from "pages/PointDetail";
import DataChilAnalyticsDetails from "pages/Project/DataChildAnalyticsDetails";
import { FC, useEffect, useRef } from "react";
import { getListPoints } from "store/actions/datamap";
import { ETypeParent } from "../../state/reducer";
interface Props {
dataOverview: any[];
title: string;
idButton: string;
}
const DownloadPDF: FC<Props> = ({ dataOverview, title, idButton }) => {
const dispatch = useAppDispatch();
const [dataPointsSelected, projectState] = useAppSelector((state) => [
state.datamap.data,
state.project.project,
]);
const pdfExportComponent = useRef<PDFExport>(null);
useEffect(() => {
if (projectState.projectnumber) {
dispatch(
getListPoints(
get(projectState, "bounds"),
projectState,
dataPointsSelected.dataSummaryPoints
)
);
}
//eslint-disable-next-line
}, [projectState]);
const renderUIPDF = (data) => {
switch (data.typeChild) {
case ETypeParent.RAW_DATA:
return (
<PointDetail
match={{
params: {
idpoint: data.pointid,
id: projectState.projectid,
isHideForExport: true,
},
}}
/>
);
case ETypeParent.ASSIGNING_GEOLOGICAL_UNITS:
return (
<ManagerGeotechnicalDetail
match={{
params: {
idpointGeotechnical: data.pointid,
id: projectState.projectid,
isHideForExport: true,
},
}}
/>
);
case ETypeParent.DATA_ANALYTICSANALYTICS:
return (
<DataChilAnalyticsDetails
match={{
params: {
idDataChildAnalytics: data.childanalysisid,
id: projectState.projectid,
isHideForExport: true,
},
}}
/>
);
default:
return;
}
};
return (
<div>
<div className="example-config">
<button
id={idButton}
className="btn btn-success mt-2 me-4 k-button k-button-md k-rounded-md k-button-solid k-button-solid-base"
onClick={() => {
if (pdfExportComponent.current) {
pdfExportComponent.current.save();
}
}}
disabled={title.length === 0}
>
Export PDF
</button>
</div>
<div className="hide-ui">
<PDFExport
paperSize="A3"
forcePageBreak=".page-break"
landscape={true}
ref={pdfExportComponent}
fileName={title}
title={title}
scale={0.6}
margin={{
top: "0.75cm",
left: "3.6cm",
right: "3.6cm",
bottom: "0.75cm",
}}
>
{dataOverview &&
dataOverview.map((data, index) => (
<div
key={index}
style={{
width: "1620px",
}}
>
{renderUIPDF(data)}
{index !== dataOverview.length - 1 && (
<div className="page-break" />
)}
</div>
))}
</PDFExport>
</div>
</div>
);
};
export default DownloadPDF;
`
The problem I am facing now is when the dataOverview list has many objects, it will map the data to the components in renderUIPDF then I will call multiple APIs at the same time in those components.
At that time, there will be lag or website freeze :(
So is there any way I can improve the performance of my website without lag, freeze when calling too many APIs at once, the number can be up to more than 100 APIs at once?
I try make an text Animation with framer motion, React
The Animation work fine still.
Since I use text Animation after switch route. So after change path.
But will be lot of components.
So I try solving things inside the component.
Not animation when path change but when the props change.
Now the Animation also work but not perfect
The essence:
The First Animation Fine with "A A"
The second and third Animation not appear one by one but at once
codesandbox
I guess based on other post something problem with stagger children. Bit I dont know exatly what should i do that working text animation one by one when props change
More simple if I show you on codesandbox:
Data.js
const data = [
{
id: 0,
maintext: "A A",
answerButtons: [{ answerText: "Next", isCorrect: true }],
image: "",
},
{
id: 1,
maintext: "B B B B",
answerButtons: [{ answerText: "Next", isCorrect: true }],
},
{
id: 2,
maintext: "C C C C C C",
answerButtons: [{ answerText: "Next", isCorrect: true }],
},
];
export default data;
MobilBase.js
import React, { useState, useEffect } from "react";
import SpeechBubble from "../../../components/SpeechBubble/SpeechBubble";
import Data from "../../../utils/data";
import "./MobilBase.css";
const MobilBase = () => {
const [counter, setCounter] = useState(0);
const [data, setData] = useState(Data);
const nextHandler = (i) => {
if (Data[counter].answerButtons[i].isCorrect === true) {
setCounter((prevState) => {
return prevState + 1;
});
}
};
return (
<div className="base base-page-grid">
<div className="counter">
<h2>
{counter} / {data.length - 1}
</h2>
</div>
<div className="speech-bubble-outer">
<SpeechBubble maintext={data[counter].maintext} />
</div>
{data[counter].answerButtons.map((answerbutton, i) => (
<div key={i} className="base-buttons-outer">
<div>
<button
onClick={() => {
nextHandler(i);
}}
>
<strong>{answerbutton.answerText}</strong>
</button>
</div>
</div>
))}
</div>
);
};
export default MobilBase;
SpeechBubble.js
import React, { useState, useEffect } from "react";
import Animation from "./Animation";
export default function SpeechBubble(props) {
return (
<div className="frame-speech-bubble">
<div className="zumzum-animation-grid-frame">
<Animation maintext={props.maintext} />
</div>
</div>
);
}
Animation.js
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion/dist/framer-motion";
import AnimatedText from "./AnimatedText";
import "./Animate.css";
export default function Animation(props) {
// Placeholder text data, as if from API
const placeholderText = [{ type: "heading1", text: props.maintext }];
const container = {
visible: {
transition: {
staggerChildren: 0.025,
},
},
};
return (
<motion.div
className="animation-frame"
initial="hidden"
animate="visible"
variants={container}
>
<div className="container-animated-text">
{placeholderText.map((item, index) => {
return <AnimatedText {...item} key={index} />;
})}
</div>
</motion.div>
);
}
AnimatedText.js
import React, { useState, useEffect } from "react";
import { motion } from "framer-motion/dist/framer-motion";
// Word wrapper
const Wrapper = (props) => {
// We'll do this to prevent wrapping of words using CSS
return <span className="word-wrapper">{props.children}</span>;
};
// Map API "type" vaules to JSX tag names
const tagMap = {
paragraph: "p",
heading1: "h1",
heading2: "p"
};
// AnimatedCharacters
// Handles the deconstruction of each word and character to setup for the
// individual character animations
const AnimatedCharacters = (props) => {
// Framer Motion variant object, for controlling animation
const item = {
hidden: {
x: "-200%",
color: "#0055FF",
transition: { ease: [0.455, 0.03, 0.515, 0.955], duration: 0.85 }
},
visible: {
x: 0,
color: "red",
transition: { ease: [0.455, 0.03, 0.515, 0.955] }
}
};
// Split each word of props.text into an array
const splitWords = props.text.split(" ");
// Create storage array
let words = [];
// Push each word into words array
for (const item of splitWords) {
words.push(item.split(""));
}
// Add a space ("\u00A0") to the end of each word
words.map((word) => {
return word.push("\u00A0");
});
// Get the tag name from tagMap
const Tag = tagMap[props.type];
return (
<Tag>
{words.map((word, index) => {
console.log(`return: word ${word} index: ${index}`);
return (
// Wrap each word in the Wrapper component
<Wrapper key={word.id}>
{words[index].flat().map((element, index) => {
return (
<span
style={{
overflow: "hidden",
display: "inline-block"
}}
key={word.id}
>
<motion.span
className="animatedtext spell"
style={{ display: "inline-block" }}
variants={item}
>
{element}
</motion.span>
</span>
);
})}
</Wrapper>
);
})}
</Tag>
);
};
export default AnimatedCharacters;
You should not use the loop index as the key in the elements output from map. Since the indices (and thus the keys) will always be the same, it doesn't let Framer track when elements have been added or removed in order to animate them.
Instead use a value like a unique id property for each element. This way React (and Framer) will know when it's rendering a different element (vs the same element with different data). This is what triggers the animations.
Here's a more thorough explanation:
react key props and why you shouldn’t be using index
I'm using React-icons in my ReactJS project and I just wanted to loop (by Map method) the specific icons in each JSX field when data is render.
In other word, I want this{`<${e.contact.icons}/>`}in JSX code.
Here is my code section:-
Here is, I import some icons for React icons.
import { FaBeer, Fa500Px, FeAccusoft } from "react-icons/fa";
Here is a data array which I want to render in JSX.
const data = [
{
contact: [
{
title: 'contact',
icons: 'FaBeer',
},
{
title: 'contact',
icons: 'Fa500Px',
},
{
title: 'contact',
icons: 'FaAccusoft',
},
],
},
]
And this is my component in down below. Which I'm using icons. You get little idea what I want to do.
const contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
<div className="text-area">
<span> {`<${e.contact.icons}/>`} </span>
</div>
</>
);
})}
</>
);
};
export default contact;
I'm trying to use like this{`<${e.contact.icons}/>`}, but is not working. When I see in browser. It's look like this.
<FaBeer/>
<Fa500Px/>
<FaAccusoft/>
It's just return like a text, but I want to get icons.
Any suggestion ?
You cannot use strings to represent React Component Types, instead you can use the imported ComponentType itself.
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
// here is data for I want to show
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer,
},
{
title: "contact",
subtitle: "get in touch",
icons: Fa500Px,
},
{
title: "contact",
subtitle: "get in touch",
icons: FaAccusoft,
},
],
},
];
const Contact = () => {
return (
<>
{data.map((e, i) => {
const Icon = e.contact.icons;
return (
<>
<div className="text-area">
<h1 className="title">{e.contact.title}</h1>
<h2 className="subtitle">{e.contact.subtitle}</h2>
<span><Icon /></span>
</div>
</>
);
})}
</>
);
};
export default Contact;
Note how the rendering of the icon changes as well. I have assigned the icon component to a variable Icon instead of calling <e.contact.icons/> directly because React expects components to start with a capital letter.
The Icon variable will be a React component (either a function component or a class component) so you can call that component by using standard JSX <Icon /> syntax. You can also pass any of the react-icons props, for example: <Icon color="#FF0000" size={24}/>.
https://codesandbox.io/s/fervent-goldwasser-y83cn?file=/src/App.js
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
// here is data for I want to show
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer
},
{
title: "contact",
subtitle: "get in touch",
icons: Fa500Px
},
{
title: "contact",
subtitle: "get in touch",
icons: FaAccusoft
}
]
}
];
const contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
{e.contact.map((e, i) => {
return (
<div className="text-area" key={i}>
<h1 className="title">{e.title}</h1>
<h2 className="subtitle">{e.subtitle}</h2>
<span>
<e.icons />
</span>
</div>
);
})}
</>
);
})}
</>
);
};
export default contact;
Well, the option of importing FaIcon-s and putting them into "data" array looks pretty nice:
import { FaBeer, Fa500Px, FaAccusoft } from "react-icons/fa";
const data = [
{
contact: [
{
title: "contact",
subtitle: "get in touch",
icons: FaBeer,
},
...
On the other hand possibility of generating components "dynamically" by their string name could be still implemented.
Firstly, I find usefull following article: React / JSX Dynamic Component Name
Next, I've created a new FaIconDynamic component:
import {
AiFillAccountBook,
AiFillAlert,
AiFillAlipayCircle,
AiFillAndroid,
} from 'react-icons/ai';
export const FaIconDynamic = ({ type }) => {
const FaIcon = components[type];
return <FaIcon></FaIcon>;
};
const components = {
AiFillAccountBook,
AiFillAlert,
AiFillAlipayCircle,
AiFillAndroid,
};
And then that's pretty easy to generate any registered above fa-icon, f.e.:
function App() {
return (
<>
<FaIconDynamic type={'AiFillAccountBook'} />
<FaIconDynamic type={'AiFillAlert'} />
<FaIconDynamic type={'AiFillAlipayCircle'} />
<FaIconDynamic type={'AiFillAndroid'} />
</>
);
}
Of course, both approaches have their pros and cons and could be more benefitial in some situations over each other
I have got the answer. I know the answer is not an ideal one, but it's work for me just now. The problem with the answer is that. We imported all the fonts from react-icons. So, I guess, as we will grow the project larger. It will decrease the performances and the major factor of could be react icons.
And also as Mr.Ali Shefaee describe in the comment section.
import React from "react";
import { render } from "react-dom";
import * as FontAwesome from "react-icons/lib/fa";
Now that section we could use two type of method.
First one :-
Here we import the all icons and use the function to get specific icon which we want
const Icon = props => {
const { iconName, size, color } = props;
const icon = React.createElement(FontAwesome[iconName]);
return <div style={{ fontSize: size, color: color }}>{icon}</div>;
};
const App = () => {
const iconString = "FaBeer";
const beer = React.createElement(FontAwesome[iconString]);
return (
<div>
<Icon iconName={"FaBeer"} size={12} color="orange" />
</div>
);
};
render(<App />, document.getElementById("root"));
And Second :-
const App = () => {
const iconString = "FaBeer";
const beer = React.createElement(FontAwesome[iconString]);
return (
<div>
<FontAwesome.FaBeer />
<div style={{ fontSize: 24, color: "orange" }}>{beer}</div>
</div>
);
};
render(<App />, document.getElementById("root"));
Here is the Demo:- Codesandbox.
Thank to〈Evie.Codes〉.
It seems that the current answers already addresses the problem, so this only attempts to add small improvement for the solution. Here is an approach I tried in a similar situation.
Simplified demo on: stackblitz
This will keep data the same as posted in the question as it might need to be fetched:
const data = [
{
contact: [
{
title: 'contact',
icons: 'FaBeer',
},
{
title: 'contact',
icons: 'Fa500Px',
},
{
title: 'contact',
icons: 'FaChrome',
},
],
},
];
Define an object with the imported icons as static data:
import { FaBeer, Fa500Px, FaChrome } from 'react-icons/fa';
const icons = { FaBeer, Fa500Px, FaChrome };
In the output, the icon can taken out from static data, and rendered on condition:
const Contact = () => {
return (
<>
{data.map((e, i) => (
<React.Fragment key={i}>
{e.contact.map((item, index) => {
const Icon = icons?.[item.icons];
return (
<div key={index} className="text-area">
<span>{Icon && <Icon size="3em" color="hotpink" />}</span>
</div>
);
})}
</React.Fragment>
))}
</>
);
};
export default contact;
import { FaBeer, Fa500Px, FeAccusoft } from "react-icons/fa";
note: there is a typo in the name of the icon you imported .. it should be FaAccusoft
my suggestion for your question is to store the Icon components themselves in the object property .. so instead of storing it as string: "FaBeer" ... store it as a component: <FaBeer /> directly inside the object property .. like this:
const data = [
{
contact: [
{
title: "contact-1",
icons: <FaBeer />
},
{
title: "contact-2",
icons: <Fa500Px />
},
{
title: "contact-3",
icons: <FaAccusoft />
}
]
}
];
and then you can simply loop over them
const Contact = () => {
return (
<>
{data.map((e, i) => {
return (
<>
{e.contact.map((c) => {
return (
<div className="text-area">
{c.title}
<span>
{c.icons} // you simply call it like this and the icon will be rendered
</span>
</div>
);
})}
</>
);
})}
</>
);
};
You can also use Parser() from html-react-parser. https://github.com/remarkablemark/html-react-parser
const parse = require('html-react-parser');
{parse(`<${e.contact.icons}/>`)};
Currently, the details of the elements which I want to display are saved at Info.js.
Parent.js is responsible for importing the details needed and then injecting them respectively into each Child.js by .map function as the info stored at Info.js is an array.
I want to dynamically display the relative Child component by the button pressed by users. For example, when the user clicked "First-Tier" button at Parent.js, only the Child.js component with the category of "First-Tier" will be shown. At this moment, my code is not working. I believe the problem is at useEffect but I cannot figure out how to fix this.
I am looking forward to receiving your inspirations. Thanks and please stay safe.
---> Parent.js
import React, { useState, useEffect } from "react";
import Info from "./Info";
import Child from "./Child";
let Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [categoryChosen, setCategoryChosen] = useState("All");
let PartsShown = [...Info];
useEffect(() => {
PartsShown = [
...PartsShown.filter((e) => e.category[1] === categoryChosen),
];
}, [categoryChosen, PartsShown]);
return (
<div>
<div>
{Category.map((element) => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => setCategoryChosen(element)}
>
{element}
</button>
))}
</div>
<div>{categoryChosen}</div>
<div>
{PartsShown.map((e) => (
<Child
key={e.name}
name={e.name}
category={e.category[1]}
/>
))}
</div>
</div>
);
};
export default Parent;
---> Child.js
import React from "react";
const Child = ({ name, category }) => (
<div style={{ margin: 10 }}>
<h1>{name}</h1>
<p>{category}</p>
<hr />
</div>
);
export default Child;
--> Info.js
const Info = [
{
name: "A",
description: "Description of A ",
category: ["All", "First-Tier"],
},
{
name: "B",
description: "Description of B",
category: ["All", "Second-Tier"],
}
];
export default Info;
import React, { useState } from "react";
import Info from "./Info";
import Child from "./Child";
const Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [partsShown, setPartsShownAndCategory] = useState({
partsArray: [...Info],
category: "All"
});
const changeCategory = category => {
const PartsShown = Info.filter(
element =>
element.category[1] === category || element.category[0] === category
);
setPartsShownAndCategory({
...partsShown,
category: category,
partsArray: PartsShown
});
};
console.log(partsShown);
return (
<div>
<div>
{Category.map(element => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => changeCategory(element)}
>
{element}
</button>
))}
</div>
<div>{partsShown.category}</div>
<div>
{partsShown.partsArray.map(e => (
<Child key={e.name} name={e.name} category={partsShown.category} />
))}
</div>
</div>
);
};
export default Parent;