I have react app with a react-spreadsheet component and a button. I am trying to get the data from the spreadsheet on the onclick() event of the button.
import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
import { TextField, Button } from '#mui/material';
import './App.css';
import Spreadsheet from "react-spreadsheet";
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
function loadBatchInput() {
alert("inside");
}
function App() {
const [tabValue, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
//batch input excel
const [batchInputData, setbatchInputData] = React.useState([
[{ value: "Type"}, { value: "Region" }, { value: "Product" }, { value: "Brand" }, { value: "Sku_Type" }],
[]
]);
return (
<div className="App">
<div className="content">
<TabPanel value={tabValue} index={0}>
<div className='rowA'>
<div>
Batch Input Table
</div>
<div>
<Button variant="contained" onClick={() => loadBatchInput() }>Load Batch Input</Button>
</div>
<div>
<Spreadsheet data={batchInputData} onChange={setbatchInputData} />
</div>
</div>
</TabPanel>
</div>
</div>
);
}
export default App;
I am trying to get the data added to the spreadsheet via the webpage inside a JavaScript function.
Is there a way to get the react-spreadsheet data(if any added via frontend) inside the loadBatchInput() on click of the Load Batch Input button
Thanks,
In your loadBatchInput() function, you will need to add your data to the 2nd element of the array of your batchInputData state. You can do so with the following:
const loadBatchInput = () => {
let batch = [...batchInputData];
batch[1] = [{value: 'data'}]
setbatchInputData(batch);
}
Related
Summary
I am trying to create todo list in ReactJS. I expect only one list item to be added per action.
Actual Behavior
When I am adding to my todolist item, it adds the item twice.
App.js
import Header from "./components/Header";
import List from "./components/List";
import {useState} from "react";
function App() {
const [items, setItem] = useState(function () {
const storageItemsString = localStorage.getItem('items');
if (storageItemsString) {
return JSON.parse(storageItemsString);
} else {
return [];
}
}
);
function addItem(fieldValue) {
setItem( (prevItems) => {
const newItems = prevItems.concat(fieldValue);
localStorage.setItem('items', JSON.stringify(newItems));
return newItems;
});
}
return (
<div className="App">
<div className="wrapper">
<Header/>
<List addItem={addItem} items={items}/>
</div>
</div>
);
}
export default App;
List jsx
import React, {useEffect} from 'react';
import {Button, TextField} from "#mui/material";
import ListItem from "./listItem";
function List(props) {
useEffect(() => {
const textField = document.querySelector('#list-field');
const button = document.querySelector('.button');
button.addEventListener('click', function(e) {
if (textField.value !== '') {
props.addItem(textField.value);
}
});
}, [props]);
return (
<div className="list-section">
<form action="#" className="list-form">
<TextField variant="outlined" label="Enter something" id="list-field" multiline
rows={4}/>
<Button variant="contained" className="button">Add item</Button>
</form>
<div className="list-items">
{props.items.map( (item) => <ListItem text={item} />)}
</div>
</div>
);
}
export default List;
ListItem jsx
import React from 'react';
import DeleteIcon from '#mui/icons-material/Delete';
function ListItem(props) {
return (
<div className="list-item">
<div className="item-content">{props.text}</div>
<span className="delete-icon">
<DeleteIcon />
</span>
</div>
);
}
export default ListItem;
I have a react-virtualized List, each item in the rows have an expand button, when the button is clicked the height of the row changes and it expands and show more info.
the issue here is, after the button got clicked the inner Grid (ReactVirtualized__Grid__innerScrollContainer) CSS height property is not updating or the Gid is not re-rendering/notified of the changes in row height
What should I do to make it rerender the row everytime the row item height gets updated?
here is my main index.js file:
import React, {
cloneElement,
Component,
useMemo,
useCallback,
useEffect,
createRef,
useRef,
useState
} from "react";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import {
List,
AutoSizer,
CellMeasurer,
CellMeasurerCache
} from "react-virtualized";
import Expander from "./expander.js";
export default function Test() {
// List data as an array of strings
let list = [
"Brian Vaughn",
"Bob Smith",
"Someone Else",
"I hate making up names"
// And so on...
];
const listRef = useRef();
const expander = (
<Expander onChanged={() => listRef.current?.list?.forceUpdateGrid()} />
);
return (
<div>
<AutoSizer
// disableWidth
// disableHeight
>
{({ width, height }) => {
return (
<List
ref={listRef}
className="List"
autoHeight
width={800}
height={400}
rowCount={list.length}
rowHeight={30}
rowRenderer={({ index, isScrolling, key, style }) => (
<div className="Row" key={key} style={style}>
{list[index]}
{expander}
</div>
)}
/>
);
}}
</AutoSizer>
</div>
);
}
Here is my expander.js file ( Row Component ):
import React, {
cloneElement,
Component,
useMemo,
useCallback,
useEffect,
createRef,
useRef,
useState
} from "react";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
export default function Expander(props) {
// State : Declare
const [isClicked, setIsClicked] = useState(false);
const renderMoreText = () => {
if (isClicked) {
return (
<div style={{ backgroundColor: "red", height: 200 }}>
long lorem ipsum{" "}
</div>
);
}
return null;
};
useEffect(() => {
props.onChanged();
}, [isClicked]);
return (
<div>
<button
onClick={() => {
setIsClicked(true);
}}
>
{" "}
Expand more text{" "}
</button>
{renderMoreText()}
</div>
);
}
Here is my sandbox:
https://codesandbox.io/s/agitated-pond-xq1csq?file=/pages/index.js
I'm not familiar with react-virtualized, but reading the docs you could make use of the List onChanged event together with Expanders recomputeRowHeights().
Updated sandbox (quick and dirty): https://codesandbox.io/s/charming-cookies-omi14k?file=/pages/index.js
This question already has answers here:
Understanding unique keys for array children in React.js
(27 answers)
Closed 4 months ago.
Currently I am working on a project using React, Next.js and Ant-Design.
While working on my project, I got an error because I didn't use a unique key like this:
Below is the detailed error log.
Warning: Each child in a list should have a unique "key" prop.
Check the render method of `ForwardRef`. It was passed a child from BoardCard. See https://reactjs.org/link/warning-keys for more information.
at eval (webpack-internal:///./node_modules/antd/es/popover/index.js:30:31)
at eval (webpack-internal:///./node_modules/antd/es/card/Card.js:58:62)
at article
at BoardCard (webpack-internal:///./components/MyPage/BoardCard.js:34:19)
at li
at InternalItem (webpack-internal:///./node_modules/antd/es/list/Item.js:65:31)
at ul
at div
at div
at Spin (webpack-internal:///./node_modules/antd/es/spin/index.js:94:90)
at SpinFC (webpack-internal:///./node_modules/antd/es/spin/index.js:222:34)
at div
at List (webpack-internal:///./node_modules/antd/es/list/index.js:58:26)
at section
at BoardList (webpack-internal:///./components/MyPage/BoardList.js:22:78)
at section
at MyBoard
at section
at MyInfo
at header
at O (webpack-internal:///./node_modules/styled-components/dist/styled-components.browser.esm.js:31:19811)
at AppLayout (webpack-internal:///./components/AppLayout/index.js:28:23)
at profile
at App (webpack-internal:///./pages/_app.js:28:24)
at Provider (webpack-internal:///./node_modules/react-redux/es/components/Provider.js:13:3)
at Wrapper (webpack-internal:///./node_modules/next-redux-wrapper/es6/index.js:184:35)
at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/#next/react-dev-overlay/client.js:8:20746)
at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/#next/react-dev-overlay/client.js:8:23395)
at Container (webpack-internal:///./node_modules/next/dist/client/index.js:241:5)
at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:833:24)
at Root (webpack-internal:///./node_modules/next/dist/client/index.js:986:26)
As a result of checking the error log, it was confirmed that the error occurred in the Boardlist and BoardCard components.
So I checked the key of the relevant component, but I am using a unique key.
import React from 'react';
import { useSelector } from 'react-redux';
import { List } from 'antd';
import BoardCard from './BoardCard';
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item>
<BoardCard post={item} key={item.id}/>
</List.Item>
)}
/>
</section>
)
};
export default BoardList;
import React, { useState, useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Card, Modal, Button, Popover, Row, Col, message } from 'antd';
import PropTypes from 'prop-types';
import PostModal from '../Modal/PostModal';
import PostDeleteModal from '../HomePost/PostDeleteModal';
import {
likePostRequestAction, unLikePostRequestAction,
moveToCommentRequestAction, returnToCommentRequestAction
} from '../../reducers/post';
import {
BoardCardHeader, BoardCardBody, MoreIcon, ColWrapper, CardTextWrapper,
CardTitle, CardText, CardContent, HeaderBtn, TwoToneHeartBtnIcon,
HeartBtnIcon, CommentBtnIcon, CardImageWrapper, ImageWrapper
} from './styles';
const BoardCard = ({ post }) => {
const dispatch = useDispatch();
const [visible, setVisible] = useState(false);
const id = useSelector((state) => state.user.me.id);
const liked = post.Likers.find((v) => v.id === id);
const onLikePost = useCallback(() => {
dispatch(likePostRequestAction(post.id));
}, []);
const unLikePost = useCallback(() => {
dispatch(unLikePostRequestAction(post.id));
}, []);
const showPostModal = useCallback(() => {
setVisible(true);
}, []);
const boardCardCommentBtn = useCallback(() => {
setVisible(true);
dispatch(moveToCommentRequestAction());
}, []);
const boardmodalOkBtn = useCallback(() => {
setVisible(false);
dispatch(returnToCommentRequestAction());
}, []);
const boardmodalCancelBtn = useCallback(() => {
setVisible(false);
dispatch(returnToCommentRequestAction());
}, []);
return (
<article>
<Card
headStyle={BoardCardHeader}
bodyStyle={BoardCardBody}
hoverable
extra={[
<Popover
trigger="click"
content={
<>
<Button>Modify</Button>
<PostDeleteModal post={post} />
</>
}
>
<MoreIcon key="ellipsis"/>
</Popover>
]}
>
<Row>
<ColWrapper xs={24} md={16}>
<CardTextWrapper>
<CardText
title={<CardTitle>{post.title}</CardTitle>}
description={post.desc}
onClick={showPostModal}
/>
<CardContent onClick={showPostModal}>{post.recipes}</CardContent>
</CardTextWrapper>
<div>
{
liked
? <HeaderBtn type='text' icon={<TwoToneHeartBtnIcon twoToneColor="#eb2f96" onClick={unLikePost} />}>{post.Likers.length}</HeaderBtn>
: <HeaderBtn type='text' icon={<HeartBtnIcon />} onClick={onLikePost}>{post.Likers.length}</HeaderBtn>
}
<HeaderBtn type='text' icon={<CommentBtnIcon />} onClick={boardCardCommentBtn} >{post.Comments.length}</HeaderBtn>
</div>
</ColWrapper>
<Col xs={24} md={8}>
<CardImageWrapper>
<ImageWrapper
alt="board image"
src={`http://localhost:3065/${post.Images[0]?.src}`}
onClick={showPostModal}
/>
</CardImageWrapper>
</Col>
</Row>
</Card>
<Modal
centered
visible={visible}
onOk={boardmodalOkBtn}
onCancel={boardmodalCancelBtn}
width={1000}
>
<PostModal post={post} />
</Modal>
</article>
);
};
BoardCard.propTypes = {
post: PropTypes.shape({
id: PropTypes.number,
User: PropTypes.object,
title: PropTypes.string,
desc: PropTypes.string,
content: PropTypes.arrayOf(PropTypes.object),
Images: PropTypes.arrayOf(PropTypes.object),
tag: PropTypes.string,
Comments: PropTypes.arrayOf(PropTypes.object),
})
};
export default BoardCard;
I tried to solve the problem, but couldn't find a way.
So, I would like to know why the above problem occurs and how to solve it.
You must put the key on the first child
const BoardList = () => {
const { boardPosts } = useSelector((state) => state.user);
return (
<section>
<List
itemLayout="vertical"
bordered
size="large"
pagination={{
onChange: (page) => console.log(page), pageSize: 3,
}}
dataSource={boardPosts}
renderItem={(item) => (
<List.Item key={item.id}>
<BoardCard post={item}/>
</List.Item>
)}
/>
</section>
)
};
When I map the array directly, I don't face any error but if I pass the same array through props, I face the error that "Cannot read property 'map' of undefined"
brands array
const brands = [
{
key: 1,
Name: "Nike",
},
{
key: 2,
Name: "Adidas",
},
{
key: 3,
Name: "Apollo",
},
{
key: 4,
Name: "Puma",
},
];
export default brands;
Parent Component
import React from "react";
import { Button, Checkbox } from "#material-ui/core";
import SimpleAccordion from "./simpleAccordion";
import "./Sidebar.css";
import brands from "./BrandNames";
function App() {
console.log("I am in App component", brands);
return (
<React.Fragment>
<div className="sidenav">
<SimpleAccordion children="Categories" brands={brands} /> // here passing the props as brands
<hr style={{ color: "#999999" }} />
{/* <h1>Helooooooo</h1>
{brands.forEach((brand) => {
return <span>{brand.Name}</span>;
})} */}
{brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
<h1>Helooooooo</h1>
<SimpleAccordion children="Brands" />
<hr style={{ color: "#999999" }} />
<SimpleAccordion children="Stores" />
<hr style={{ color: "#999999" }} />
<SimpleAccordion children="Price" />
</div>
</React.Fragment>
);
}
export default App;
Child Component
import React,{useState} from 'react';
import Accordion from '#material-ui/core/Accordion';
import AccordionSummary from '#material-ui/core/AccordionSummary';
import AccordionDetails from '#material-ui/core/AccordionDetails';
import Typography from '#material-ui/core/Typography';
import AddIcon from '#material-ui/icons/Add';
import CloseIcon from '#material-ui/icons/Close';
import {Checkbox} from '#material-ui/core';
import brands from './BrandNames';
export default function SimpleAccordion(props) {
const[iconName,setIcon]= useState(false);
function handleClick(){
setIcon(!iconName);
}
// shows data at first time but then becomes undefine
console.log("here", props.brands)
return (
<React.Fragment>
// Directly mapping the (imported)"brands" array, no error
{brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
// mapping props.brands array and there are errors.
{props.brands.map((brand, key) => {
return (
<div>
<h1>{brand.Name}</h1>
<h1>{brand.key}</h1>
</div>
);
})}
<Accordion style={{boxShadow:"0px 0px 0px "}} >
<AccordionSummary expandIcon={iconName ? <CloseIcon /> : <AddIcon/>} onClick={handleClick}>
<Typography style={{color:"#333333",fontWeight:"bolder"}}>{props.children}</Typography >
</AccordionSummary>
<AccordionDetails >
<Typography>
</Typography>
</AccordionDetails>
</Accordion>
</React.Fragment>
);
}
If I don't use the props in the child component, it works fine. brands.map works fine but when props are used, props.brands give the errors i.e TypeError: Cannot read property 'map' of undefined. Why are the props giving me the errors...
I was rendering more than one SimpleAccordion component but only passing props to the first component. That's why the props were undefined when mapped as other components were not given the props.
I have implemented Material UI's tabs successfully by hard-coding the content, but when I tried to make a my hard coded tabs with a .map function to populate the content from a data source (simple json), it no longer works. Can anyone see why? The only change I made was to the MyTabs component below where there are now two .map functions instead of hard coded tabs.
Many thanks for your help!
Here is my data:
export const TabsData = [
{
tabTitle: 'Tab 1',
tabContent: 'Hello 1',
},
{
tabTitle: 'Tab 2',
tabContent: 'Hello 2',
},
{
tabTitle: 'Tab 3',
tabContent: 'Hello 3',
},
];
Here is my MyTabs component:
import React, { useState } from 'react';
// Material UI
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
// Data
import { TabsData } from '../../page-templates/full-page-with-tabs/FullPageWithTabsData';
//Components
import TabContentPanel from '../tabs/tab-content-panel/TabContentPanel';
const MyTabs = () => {
const classes = useStyles();
const initialTabIndex = 0;
const [value, setValue] = useState(initialTabIndex);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<>
<Tabs
value={value}
onChange={handleChange}
aria-label=""
className={classes.tabHeight}
classes={{ indicator: classes.indicator }}
>
{TabsData.map((tabInfo, index) => (
<>
<Tab
label={tabInfo.tabTitle}
id={`simple-tab-${index}`}
ariaControls={`simple-tabpanel-${index}`}
/>
</>
))}
</Tabs>
{TabsData.map((tabInfo, index) => (
<TabContentPanel value={value} index={index}>
{tabInfo.tabContent}
</TabContentPanel>
))}
</>
);
};
export default MyTabs;
And here is the TabsPanel component:
import React from 'react';
import PropTypes from 'prop-types';
// Material UI
import { Box } from '#material-ui/core';
function TabContentPanel(props) {
const { children, value, index, ...other } = props;
const classes = useStyles();
return (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && <Box className={classes.contentContainer}>{children}</Box>}
</div>
);
}
TabContentPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
export default TabContentPanel;
It doesn't work because you added some extra Fragments (<> and </>) in the Tabs component and the Tabs component doesn't accept a Fragment as a child:
If you remove those, it will work as expected:
{TabsData.map((tabInfo, index) => (
<Tab
label={tabInfo.tabTitle}
id={`simple-tab-${index}`}
key={tabInfo.tabTitle}
ariaControls={`simple-tabpanel-${index}`}
/>
))}
And please use the key prop with a unique id if you create an array of elements. Each child in a list should have a unique "key" prop.