props are passed but are undefined when they are mapped - javascript

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.

Related

React: Get all data from Table from a onclick function

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);
}

properties.map is not a function when map

I'm getting the error "TypeError: properties.map is not a function", however when i console.log(properties) i can see the data. Can anyone explain me what i'm doing wrong?
If i remove the map code obviously the error is not showing. Is it because "properties" is not defined yet during the render? I'm a bit confused of why it's not defined since as i said i can see the data on load with the console.
import React, { Component } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AndroidOutlined, AppleOutlined } from '#ant-design/icons';
import Tabs, { TabPane } from '#iso/components/uielements/tabs';
import Select, { SelectOption } from '#iso/components/uielements/select';
import Button from '#iso/components/uielements/button';
import PageHeader from '#iso/components/utility/pageHeader';
import Box from '#iso/components/utility/box';
import { Form, Input, Checkbox, Col, Row } from 'antd'
import LayoutWrapper from '#iso/components/utility/layoutWrapper.js';
import ContentHolder from '#iso/components/utility/contentHolder';
import IntlMessages from '#iso/components/utility/intlMessages';
import FormGeneral from './FormGeneral';
import FormDetails from './FormDetails';
import propertiesActions from '#iso/redux/properties/actions';
const Option = SelectOption;
const {
loadFromApi,
} = propertiesActions;
export default function PropertiesPage() {
const dispatch = useDispatch();
React.useEffect(() => {
dispatch(loadFromApi());
}, [dispatch]);
const { properties, property, isLoading } = useSelector(
state => state.Properties
);
const rowStyle = {
width: '100%',
display: 'flex',
flexFlow: 'row wrap',
};
const colStyle = {
marginBottom: '16px',
};
const gutter = 16;
// Show data
console.log(properties);
console.log(isLoading);
return (
<LayoutWrapper>
<PageHeader>Properties</PageHeader>
<Box>
<Tabs defaultActiveKey="1">
<TabPane
tab={
<span>
<AppleOutlined />
General
</span>
}
key="1"
>
<div>
{console.log(properties)}
{properties.map(e =>
<div key={e.id}>
{e.id}
</div>
)}
</div>
<Row style={rowStyle} gutter={gutter} justify="start">
<Col md={12} sm={12} xs={24} style={colStyle}>
<Box>
<FormGeneral />
</Box>
</Col>
</Row>
</TabPane>
<TabPane
tab={
<span>
<AndroidOutlined />
Tab 2
</span>
}
key="2"
>
<FormDetails />
</TabPane>
</Tabs>
</Box>
</LayoutWrapper>
);
}
UPDATED:
properties is empty object at first
What if you try properties?.map, so it doesn't map if it is undefined, and wait until you really get the array. Because I don't have a big knowledge about redux, but from what I can read from your code, I understand that you are getting the properties asynchronously.
By default properties is undefined. .map cannot work with undefined. Try to use:
const { properties = [], property, isLoading } = useSelector(
state => state.Properties
);
It is possible to extend the answer and change the initialState, but your reducer code is needed.
before you map properties you should check if it is undefined because the initial value of state object in react is undefined and I think that if you will roll up in your console you should see an undefined print to the console
<div>
{console.log(properties)}
{properties !== undefined ? properties.map(e =>
<div key={e.id}>
{e.id}
</div>
): null}
</div>
In fact the object was defined but empty, i had to check for the length
<div>
{console.log(properties)}
{properties.length > 1 ? properties.map(e =>
<div key={e.id}>
{e.id}
</div>
): null}
</div>
Doing this resolved the issue.

How to share state between React function and re-render on changes

I have a react component that renders a forms with checkboxes and a chart (Victory). I want the component to show the active checkboxes in the graph (each checkboxes has its api url, for simplicity, the code below shows useless data and no fetching). There are multiple problems in this code:
1) The activestock state always seem to be one step behind in the point of view of the console.log, in the console it show the previous state instead of the actual content of the array. But in the React Dev Tools it shows the activestock correctly.
2) The VictoryChart doesnt get re-rendered even though its props (which is the activestock) changes, even though I can see in the React Dev Tools that multiple VictoryLines components exist. It's like the parents doesn't rerender on state changes, but I thought that by passing a state in props, you shared state between components?
3) Because the hooks rerenders the components, the checkboxes doesn't show a check when clicked.
Here is a sandbox link: https://codesandbox.io/s/crazy-murdock-3re1x?fontsize=14&hidenavigation=1&theme=dark
import React, {useState, useEffect}from 'react'
import Card from 'react-bootstrap/Card';
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import { VictoryLine, VictoryChart } from 'victory';
export default function HomeCards(props){
const [activestock, setActiveStock] = useState([])
function handleActiveStocks(props){
if (activestock.includes(props) === false){
setActiveStock([...activestock, props])
console.log(activestock)
}
else {
setActiveStock(activestock.filter(activestock => activestock !== props))
console.log(activestock)
}
}
function Lines(props){
const charts = []
if (props.chart !== undefined){
props.chart.map(function(val, index){
charts.push(
<VictoryLine data={[
{x:index+1, y:index+2},
{x:index+2, y:index+4}
]}/>
)
})
return charts
}
return null
}
function RealCharts(props){
return(
<VictoryChart>
<Lines chart={props.stocks}></Lines>
</VictoryChart>
)
}
function Forms(props){
const hg=[]
props.text.map(function(val, index){
hg.push(
<form>
{val} <input type='checkbox' onClick={()=> handleActiveStocks(val)}/> {index}
</form>
)
})
return(hg)
}
return(
<Container fluid>
<Row style={{position:'relative', top:'2vh'}}>
<Card style={{width:props.width}}>
<Card.Header>U.S Equity</Card.Header>
<Forms
text={['S&P 500', 'NASDAQ',' DOW', 'Russel 1000', 'Large Cap', 'Small Cap', 'MSFT', 'FB']}
/>
</Card>
<Card>
{/* <VictoryChart >
<Lines chart={activestock}/>
</VictoryChart> */}
<RealCharts stocks={activestock}/>
</Card>
</Row>
</Container>
)
}
You need a checked={some_value_here} for checkbox to be checked.
import React, { useState, useEffect } from "react";
import Card from "react-bootstrap/Card";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import { VictoryLine, VictoryChart } from "victory";
export default function HomeCards(props) {
const [activestock, setActiveStock] = useState([]);
function handleActiveStocks(props) {
if (activestock.includes(props) === false) {
setActiveStock([...activestock, props]);
console.log(activestock);
} else {
setActiveStock(activestock.filter(activestock => activestock !== props));
console.log(activestock);
}
}
function Lines(props) {
const charts = [];
if (props.chart !== undefined) {
props.chart.map(function(val, index) {
charts.push(
<VictoryLine
data={[
{ x: index + 1, y: index + 2 },
{ x: index + 2, y: index + 4 }
]}
/>
);
});
return charts;
}
return null;
}
function RealCharts(props) {
return (
<VictoryChart>
<Lines chart={props.stocks} />
</VictoryChart>
);
}
// useEffect()
function Forms(props) {
const hg = [];
props.text.map(function(val, index) {
console.log(activestock)
// check the value exists in the activeStock
const checked=activestock.includes(val);
hg.push(
<form>
{val}{" "}
<input type="checkbox" checked={checked} onClick={() => handleActiveStocks(val)} />{" "}
{index}
</form>
);
});
return hg;
}
return (
<Container fluid>
<Row style={{ position: "relative", top: "2vh" }}>
<Card style={{ width: props.width }}>
<Card.Header>U.S Equity</Card.Header>
<Forms
text={[
"S&P 500",
"NASDAQ",
" DOW",
"Russel 1000",
"Large Cap",
"Small Cap",
"MSFT",
"FB"
]}
/>
</Card>
<Card>
{/* <VictoryChart >
<Lines chart={activestock}/>
</VictoryChart> */}
<RealCharts stocks={activestock} />
</Card>
</Row>
</Container>
);
}
The shared state is not wrong, you just need to add necessary check before return it.

how to render components based on the value of props?

I have a parent component Dashboard.js. Here I have three values of state namely yesterday, lastWeek, lastMonth and I'm passing this value to my child component. Now I want to render my data depending on my child component. The problem is I'm using componentDidMount() lifecycle method which is rendering the child component only once. How do I render the data based on the props passed to the child component? The parent component is a react hook and the child component called DataFetchDetails is a class based component. Attaching their respective codes
Parent Component :- Dashboard.js
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import { Tabs, Tab, Grid } from '#material-ui/core';
import AppBar from '#material-ui/core/AppBar';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import PropTypes from 'prop-types';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select'
import {
TotalUsers,
LoggedInUsers,
TimePicker,
UnregisteredUsers
} from './components';
import DataFetchDetails from './components/DataFetchDetails';
const useStyles = makeStyles(theme => ({
root: {
paddingTop: theme.spacing(4),
padding: theme.spacing(4)
},
formControl: {
margin: theme.spacing(1),
minWidth: 120,
},
selectEmpty: {
marginTop: theme.spacing(2),
},
}));
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
<Box p={3}>{children}</Box>
</Typography>
);
}
function a11yProps(index) {
return {
id: `simple-tab-${index}`,
'aria-controls': `simple-tabpanel-${index}`,
};
}
const Dashboard = () => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const [period, setPeriod] = React.useState("yesterday");
const handleChange1 = event => {
setPeriod(event.target.value);
};
return (
<div className={classes.root}>
<Select
labelId="demo-simple-select-label"
id="demo-sample-select"
value={time}
onChange={handleChange1}
>
<MenuItem value={"yesterday"}>Previous day</MenuItem>
<MenuItem value={"lastWeek"}>Last Week</MenuItem>
<MenuItem value={"lastMonth"}>Last Month</MenuItem>
</Select>
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={handleChange} aria-label="simple tabs example">
<Tab label="CONSENT DETAILS" {...a11yProps(0)} />
<Tab label="ACCOUNT DETAILS" {...a11yProps(1)} />
<Tab label="DATA FETCH DETAILS" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
</TabPanel>
<TabPanel value={value} index={1}>
</TabPanel>
<TabPanel value={value} index={2}>
<DataFetchDetails period={period} handlePeriodChange1={handleChange1} />
</TabPanel>
</div>
</div>
);
};
export default Dashboard;
Child component DataFetchDetails.js :-
import React from 'react';
import {
Card,
CardHeader,
Button,
Divider,
CardContent,
TextField,
CardActions,
FormControl,
InputLabel,
Select,
MenuItem
} from '#material-ui/core';
import Paper from '#material-ui/core/Paper';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import axios from 'axios';
import 'izitoast/dist/css/iziToast.min.css'; // loading css
import iziToast from 'izitoast/dist/js/iziToast.min.js'; // you have access to iziToast now
import 'izitoast/dist/css/iziToast.min.css';
const url = 'MY_ENDPOINT_URL';
export default class DataFetchDetails extends React.Component {
constructor(props) {
super(props);
this.state = {
items : [],
isLoaded : true,
renderJsx: false,
}
}
componentDidMount() {
this.setState({period: this.props.period});
const periodStatus = {
period : this.props.period
};
{console.log("Props period = ",this.props.period)}
axios.post(url, periodStatus)
.then((response) => {
this.setState({period : this.props.period})
this.setState({items : [response.data]});
.catch((error) => {
console.log("Error");
});
}
render() {
let {isLoaded, items, renderJsx } = this.state;
if(!isLoaded) {
return <div>Loading</div>
}
else {
return (
<div>
<div>
<Card className="Lock-user"
>
<form >
<CardHeader
title=""
/>
<Divider></Divider>
<CardContent id="form-input" className=""
>
</CardContent>
<Divider></Divider>
</form>
</Card>
</div>
<div>
<Card>
<Paper>
<Table>
<TableHead>
<TableRow>
<TableCell> success </TableCell>
<TableCell align="right">failure</TableCell>
<TableCell align="right">inProgress</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ items.map(item => (
<TableRow key={item.success}>
<TableCell component="th" scope="row">
{item.success}
</TableCell>
<TableCell align="right">{item.failure}</TableCell>
<TableCell align="right">{item.inProgress}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{console.log("Props period render = ",this.props.period)}
</Paper>
</Card>
</div>
</div>
);
}
}
}
the backend and the api works fine. I want to re render my child component based on the value of the period. How do I solve this problem?
You you compare the props i.e prevProps and current props(this.props) object inside
ComponentDidUpdate
lifecycle method to re-render the child component based on props.
As ComponentWillReceiveProps is deprecated now.
https://egghead.io/lessons/react-refactor-componentwillreceiveprops-to-getderivedstatefromprops-in-react-16-3
Go through the react lifecycle docs or https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/postrender_with_componentdidupdate.html.
Use componentWillRecieveProps in child component.
componentWillRecieveProps(props) {
// props => new props passed
// this.props => current props
}
I hope that helps.

How to render post in different layout getting from same parent component in reactjs

I am trying to render posts with different style in the same child component something like this.
I am getting posts from wordpress api in home component then passed to renderedPost component where posts are being maped and then passed to PostPreview where it is being displayed. Here i want to display posts like i have provided in the above picture.
This is my code .
src/views/home.js
import React, { Component } from 'react';
import Layout from '../layouts';
import { connect } from 'react-redux';
import { fetchPosts } from '../actions/postActions';
import PropTypes from 'prop-types';
import RenderPost from '../components/RenderPost';
class Home extends Component {
componentWillMount() {
this.props.fetchPosts();
}
render() {
console.log(this.props.posts)
return (
<Layout>
{this.props.posts && <RenderPost posts={this.props.posts}/>}
</Layout>
);
}
}
Home.prototypes = {
fetchPosts: PropTypes.func.isRequired,
posts: PropTypes.array.isRequired
}
const mapStateToProps = state => ({
posts: state.posts.items,
})
export default connect(mapStateToProps, {fetchPosts})(Home);
src/component/renderedPost.js
import React from 'react'
import PostPreview from './PostPreview';
export default function RenderPost(props) {
console.log(props);
return (
<>
{props.posts.map(post => <PostPreview
title={post.title.rendered}
key={post.id}
image={post.imageURL}
/>)}
</>
)
}
component/PostsPreview.js // now here i am confused how to render posts like in the above picture.
import React from 'react'
export default function PostPreview({title}) {
return (
<div>
<h1>{title}</h1>
</div>
)
}
You could set display to flex on the post container, and add a new prop called e.g. imageLeft that you set to true for every other post and use that to alternative the flex-direction between row and row-reverse to alternate the image position.
Example
class Home extends React.Component {
state = {
posts: [
{ id: 0, title: "foo", imageURL: "https://placekitten.com/200/200" },
{ id: 1, title: "bar", imageURL: "https://placekitten.com/200/200" },
{ id: 2, title: "baz", imageURL: "https://placekitten.com/200/200" }
]
};
render() {
return (
<div>
<RenderPost posts={this.state.posts} />
</div>
);
}
}
function RenderPost(props) {
return (
<div>
{props.posts.map((post, index) => (
<PostPreview
key={post.id}
imageLeft={index % 2 === 0}
title={post.title}
image={post.imageURL}
/>
))}
</div>
);
}
function PostPreview({ image, imageLeft, title }) {
return (
<div
style={{
display: "flex",
width: 200,
flexDirection: imageLeft ? "row" : "row-reverse"
}}
>
<div
style={{
backgroundImage: `url(${image})`,
backgroundSize: "cover",
flexGrow: 1
}}
/>
<h1>{title}</h1>
</div>
);
}
ReactDOM.render(<Home />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Categories

Resources