How to put React hooks into for loop? - javascript

I want append a div container with divs. The divs quantity is the data json's length. I have tried to put the hooks inside the for loop, but it throwed error.
The project's github link:
https://github.com/folza1/react-modulzaro
import "./App.css";
import React, { useEffect, useRef } from "react";
function App() {
var data = require("./data.json");
//for(let i=0;i<data.length;i++{WHAT COPY HERE TO APPEND THE 30 divProduct?})
const container = useRef(null);
const divProduct = document.createElement("div");
divProduct.classList.add("divStyle");
useEffect(() => {
container.current.appendChild(divProduct);
}, []);
return (
<div>
<div id={"container"} ref={container} />
</div>
);
}
export default App;

The example code posted seems somewhat confused with regards to how React works.
Assuming data is an array of objects, all you would need for this is something like the following:
import "./App.css";
import * as React from "react";
const data = [
{id: "1", name: "obj1"},
{id: "2", name: "obj2"},
{id: "3", name: "obj3"}
];
function App() {
return (
<div id="container">
{data.map(obj => <p key={obj.id}>{obj.name}</p>)}
</div>
);
}
export default App;

Related

How to clone data from a passable props on React JS

I'm creating a Multi Dropdown component in React.JS, I want to clone a variable (selectedData) from App.js into a component. But when I try to clone data there is always an error "Cannot assign to read only property 'selectedData' of object"
import React from 'react';
import MultiDropdown from './Components/MultiDropdown/MultiDropdown.component';
import { allOptions } from './Utils/DummyData';
import "./App.css";
const App = () => {
var clonedData = [
{ value: 'Normal😐', label: 'Normal😐' },
{ value: 'Angry😡', label: 'Angry😡' },
{ value: 'Love😍', label: 'Love😍' },
]
return(
<div className='app'>
<MultiDropdown
data={allOptions}
placeholder="Select Options"
selectedData={clonedData}
// value={clonedData}
/>
<button onClick={() => console.log("Selected", clonedData)}>Click to See SelectedData</button>
</div>
)
}
export default App;
I wanted to clone variable CloneData, that passed on selectedData, I use this function to clone data
Here's my components code :
export default function MultiDropdown(props: Props): React.Node {
const [data, setData] = React.useState(props.selectedData ? props.selectedData.map(opt => opt.value) : []);
React.useEffect(() => {
props.selectedData = data;
}, [data, props]);
return (
<div>
<Select
ref={props.selectedData}
{...DropDownProps(props, data, SelectOption)}
onChange={selected => setData(selected.map(opt => opt.value))}
/>
{data.map(opt => (<ListContainer key={opt} opt={opt} data={data} set={setData} />))}
</div>
);
}
I'm trying cloning my variable on useEffect
Thankyou guys!
You can't directly change props that come to your component but there is a way:
You can create a useState to store your clonedData pass the state and the function that changes that state.
import React from 'react';
import MultiDropdown from './Components/MultiDropdown/MultiDropdown.component';
import { allOptions } from './Utils/DummyData';
import "./App.css";
const App = () => {
const [clonedData , setClonedData] = React.useState([
{ value: 'Normal😐', label: 'Normal😐' },
{ value: 'Angry😡', label: 'Angry😡' },
{ value: 'Love😍', label: 'Love😍' },
]);
return(
<div className='app'>
<MultiDropdown
data={allOptions}
placeholder="Select Options"
selectedData={clonedData}
changeSelectedData={setClonedData} // pass the setter function.
// value={clonedData}
/>
<button onClick={() => console.log("Selected", clonedData)}>Click to See SelectedData</button>
</div>
)
}
export default App;
Then use this useState hook rather than defining it in the component. Because there is no way to directly pass anything defined in the child component to the parent component

Why I can not initialize value in useState?

tiles is an array of objects
var intialTiles = tiles;
const [newTiles, setNewTiles] = useState(intialTiles);
when I console log newTiles i see undefined , what could be the reson ?
You can send the props to the component this way, <App tiles={[123, 4546]} />
And in the App component,
import "./styles.css";
import { useState, useEffect } from "react";
export default function App(props) {
var intialTiles = props.tiles;
const [newTiles, setNewTiles] = useState(intialTiles);
useEffect(() => {
console.log(newTiles);
}, [newTiles]);
function update() {
setNewTiles([...newTiles, parseInt(Math.random() * 2345, 10)]);
}
return (
<div className="App">
<button onClick={update}>update</button>
<h2>Please check the console output</h2>
</div>
);
}
Hope it helps! live demo here: https://codesandbox.io/s/cocky-bash-23o700?file=/src/App.js:0-506

How to give a new REDUIX item a unique ID? [duplicate]

This question already has answers here:
How do I create a GUID / UUID?
(70 answers)
Closed 12 months ago.
I am mapping an array of data with props into a component. Then onClick I pull some of that data into redux/reducer from the rendered items, trying to render the same data - but in a different spot on the page.
My problem is (I assume?) that the ID's are the same - I render data with keys's/id's that were already taken - while React wants unique ones.
I am not sure, if that's what's causing the problem - but I keep getting a warning that react wants unique key props.
(it's a shop app - on click, i want to add the chosen item to a cart with redux... )
Thoughts?
here I am building the component to render
import { useDispatch, useSelector } from 'react-redux'
import { add } from '../features/addToCart'
export const ReduxshopProps = (props) => {
const dispatch = useDispatch()
const handleAddToCart = (props) => {
dispatch(add(props));
};
return (<>
<div key={props.id} className='shopitem'>
<img src={props.url} />
<h2>{props.title}</h2>
<p className='boldprice'>${props.price}</p>
<button onClick={() => handleAddToCart(props) }
>
ADD TO CART
</button>
</div>
</>
)
}
here I am passing data into the component
import React from "react"
import { ReduxshopProps } from "./ReduxshopProps"
import shopdata from "./shopdata"
export default function ReduxShop() {
const cards = shopdata.map(props => {
return (
<ReduxshopProps
key={props.id}
title={props.title}
price={props.price}
url={props.url}
/>
)
})
return (
<div className='shopwrapper'>
<h1>TradingView Indicators</h1>
<div className='itemWrapper'>
{cards}
</div>
</div>
)
}
here's the REDUX code that pulls data from above
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
cartItems: [],
cartTotalQuantity: 0,
cartTotalAmount: 0,
}
export const addToCartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
add(state, action ) {
const itemIndex = state.cartItems.findIndex(
(item) => item.id === action.payload.id
);
if(itemIndex >= 0){
state.cartItems[itemIndex].cartQuantity += 1
} else {
const tempProduct = {...action.payload, cartQuantity: 1}
state.cartItems.push(tempProduct);
}
},
},
});
export const {add} = addToCartSlice.actions;
export default addToCartSlice.reducer;
and here I'm trying to render the data when someone clicks on a button.. onClick it acts as all components have the same ID - also I'm getting the key prop error from here, below
import React from 'react'
import { useSelector } from 'react-redux'
function Cart() {
const cart = useSelector((state) => state.cart)
return (
<div>
<h1>Cart</h1>
{cart.cartItems.map(cartItem => (
<div key={cartItem.id}>
<p>product : {cartItem.title}</p>
<p>price {cartItem.price}</p>
<p>quantity : {cartItem.cartQuantity}</p>
<p>url : <img src={cartItem.url} /></p>
</div>
))}
</div>
)
}
export default Cart
What you are trying to do is, assign UUID
First in terminal:
npm install uuid
Then:
import { v4 as uuidv4 } from 'uuid';
uuidv4(); // ⇨ '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d'
More on here, a sof thread: How to create a GUID / UUID
The library, on npm: https://www.npmjs.com/package/uuid

Tabulator in React - TypeError: Cannot read property 'tagName' of undefined

I am getting this error...
5440 | this.bindModules();
5441 |
> 5442 | if (this.element.tagName === "TABLE") {
| ^ 5443 | if (this.modExists("htmlTableImport", true)) {
5444 | this.modules.htmlTableImport.parseTable();
5445 | }
When I attempt to use the Tabulator library in a React component.
import React, { useState, useEffect } from "react";
import Tabulator from "tabulator-tables";
import "tabulator-tables/dist/css/tabulator.min.css";
function Journal(props) {
let refTable = React.createRef();
const [journalItems, setJournalItems] = useState([]);
useEffect(() => {
new Tabulator(refTable, {
data: journalItems,
reactiveData: true,
columns: ["a", "b", "c"],
});
}, []);
return (
<div>
<div className="foo" ref={refTable}></div>
</div >
)
}
export default Journal;
The library example uses the class component approach, whereas I want to use the functional one.
What am I doing wrong?
I have fixed the issue here - https://codesandbox.io/s/gallant-wright-365ur .
createRef is not supposed to be used in functional components. Reference - What's the difference between `useRef` and `createRef`?
Only change needed is -
<div className="foo" ref={el => (refTable = el)} />
Hope this helps.
I managed to solve this like so...
import React, { useState, useEffect } from "react";
import Tabulator from "tabulator-tables";
import "tabulator-tables/dist/css/tabulator.min.css";
function Journal(props) {
let refTable = useRef(null);
let table = useRef(null);
const [journalItems, setJournalItems] = useState([]);
useEffect(() => {
table.current = new Tabulator(refTable.current, {
data: journalItems,
reactiveData: true,
columns: ["a", "b", "c"],
});
}, []);
return (
<div>
<div className="foo" ref={refTable}></div>
</div >
)
}
export default Journal;
This was the problem.
Assignments to the 'table' variable from inside React Hook useEffect will be lost after each render.
To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property.
Otherwise, you can move this variable directly inside useEffect. (react-hooks/exhaustive-deps)

Link outside a Router Error, while everything set up properly

Ok, I have no idea why this is not working. Everything is set up properly from what I can see.
I am using "react-router-dom": "^5.0.0"
The code also uses the Tabulator grid library, specifically the React implementation of it. It's not really relevant, just wanted to note it.
The code works 100% without using the sub-component links, so the problem is not there.
The grid generator in Journals creates a table, which has link cells, which lead to the Journal component.
The link component is generated fine, it just doesn't work for reasons I don't know.
CodeSandbox
If you comment out the formatter line in columns in the Journal component, the app works again.
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Header from './components/layout/Header';
import Dashboard from './components/pages/Dashboard';
import Journals from './components/pages/Journals';
import Journal from './components/pages/Journal';
class App extends Component {
render() {
return (
<Router>
<div className="App">
<div className="container">
<Header />
<div className="content">
<Route exact path="/" component={Dashboard} />
<Route exact path="/journals" component={Journals} />
<Route path="/journals/:key" component={Journal} /> // <------ ROUTE IS HERE
</div>
</div>
</div>
</Router>
);
}
}
export default App;
Journals.js
import React, { useState, useEffect } from "react";
import { Link } from 'react-router-dom';
import { ReactTabulator } from 'react-tabulator'
import "tabulator-tables/dist/css/tabulator.min.css";
import { reactFormatter } from 'react-tabulator';
function Journals() {
const [journals, setJournals] = useState([]);
useEffect(() => {
fetch("http://localhost:4000/journals")
.then(res => res.json())
.then(data => {
setJournals(data)
})
.catch(err => err);
}, []);
const JournalLink = (props) => {
const cellData = props.cell._cell.row.data;
let key = cellData.key_
let link = `/journals/${key}`
return <Link to={link}>{key}</Link>; // <------ LINK COMPONENT IS HERE
}
const columns = [
{
title: "Number",
field: "key_",
formatter: reactFormatter(<JournalLink />) // <------ LINK COMPONENT USED HERE
},
{ title: "Date", field: "date_" },
];
return (
<div>
<h1>Journals</h1>
<ReactTabulator
data={journals}
columns={columns}
tooltips={true}
layout={"fitData"}
/>
</div >
)
}
export default Journals;
reactFormatter usage example
reactFormatter definition
Journal.js
import React, { useState, useEffect } from "react";
import { ReactTabulator } from 'react-tabulator'
import "tabulator-tables/dist/css/tabulator.min.css";
function Journal(props) {
const [journalItems, setJournalItems] = useState([]);
const initialFormJournalItems = {
id: "",
journalId: "",
companyId: "",
documentKey: "",
documentDate: "",
debitAccount: "",
debit: "",
creditAccount: "",
credit: ""
}
const [formJournalItems, setFormJournalItems] = useState(initialFormJournalItems);
useEffect(() => {
fetch(`http://localhost:4000/journals/${props.match.params.key}`)
.then(res => res.json())
.then(data => {
setJournalItems(data)
})
.catch(err => err);
}, []);
const columns = [
{ title: "Document", field: "documentKey" },
{ title: "Date", field: "documentDate" },
];
return (
<div>
<h1>Journal</h1>
<ReactTabulator
data={journalItems}
columns={columns}
tooltips={true}
layout={"fitData"}
/>
</div >
)
}
export default Journal;
react-tabulator reFormatter is incompatible with react-router library.
https://github.com/ngduc/react-tabulator/blob/0.10.3/lib/Utils.js#L30
From source code,
function reactFormatter(JSX) {
return function customFormatter(cell, formatterParams, onRendered) {
//cell - the cell component
//formatterParams - parameters set for the column
//onRendered - function to call when the formatter has been rendered
onRendered(function () {
var cellEl = cell.getElement();
var CompWithMoreProps = React.cloneElement(JSX, { cell: cell });
react_dom_1.render(CompWithMoreProps, cellEl.querySelector('.formatterCell'));
});
return '<div class="formatterCell"></div>';
};
}
rendering of a formatted element uses the ReactDOM.render function to render the formatted element directly to DOM isolated from parent elements.
A fix to react-tabulator needs to be done to support this use case. One way to go is to have customFormatter return a custom component that provides a way to set its state from outside it. Then onRendered can call this function to set cell.

Categories