For some reason, I just can't get JSON strings to display in my component. I've tried various solutions, but no luck.
Below is my component where I map over my items:
UnitEventsItems.js
const GetEventsItems = (props) => {
const { parsed = {} } = props;
const getEventsItems = Object.entries(parsed).map((item, i) => ({
item_Id: i,
label: item[0],
info: item[1]
}));
return (
<div style={{
marginBottom: theme.spacing(4)
}}
>
{getEventsItems.map((eventsItems) => (
...
<Typography>
{`${eventsItems.info}`}
</Typography>
</Box>
Below is the component where I import getEventItems for display:
EventsBlock.js
import GetEventsItems from './UnitEventsItems';
...
const EventsBlock = (props) => {
const classes = useStyles(props);
const {
eventsData,
type
} = props;
return (
<>
{
...
<Paper className={clsx(classes.paper, classes.scrollBar)}>
<List component="nav" aria-label={`${type} Events`}>
{eventsData.map((item, i) => (
<ListItem key={`${i + 1}${item.type}_${item.trixid}`}>
<GetEventsItems
parsed={item.comment}
/>
</ListItem>
))}
</List>
</Paper>
And then this is the parent where I import EventBlock.js and pass the props:
index.js
import React from 'react';
import EventsBlock from './EventsBlock';
const UnitEvents = (props) => {
const { eventsData } = props;
const refueling = (eventsData?.Refueling ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
const periodic = (eventsData?.Periodic ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
const corrective = (eventsData?.Corrective ?? []).map((event) => (event?.comment ? JSON.parse(event.comment) : {}));
console.log('periodic:', periodic);
console.log('refueling:', refueling);
console.log('corrective:', corrective);
return (
<>
<EventsBlock type="Periodic" eventsData={periodic} />
<EventsBlock type="Refueling" eventsData={refueling} />
<EventsBlock type="Corrective" eventsData={corrective} />
</>
);
};
export default UnitEvents;
Below is an image of what data looks like coming through from the API:
Below is what I have logged in the console as shown in the code above:
This is live example of code Sandbox
What is wrong on your code
In the EventsBlock component you need to have a check Object.keys(eventsData).length after that Draw eventsData.map
Inside of eventsData.map you need also some check before draw GetEventsItems , something like
{ item.comment && (
<li>
<GetEventsItems parsed={item.comment } />
</li>
)
Check this example , I have wrote your logic with other json data , try to use that aproach with your json data. code
Problem solved by making the following the change.
Changed this:
<GetEventsItems parsed={item.comment} />
to this:
<GetEventsItems parsed={item}/>
Related
React, typescript, and jest are all new to me. I have a component, 'myContacts,' displaying tabs and tab panels with contacts; an address book.
Here is a portion of myContacts.
import ... as needed ...
export default function myContacts( props ){
const { data, link } = props;
const [limit, setLimit] = useState<number>(data.limit);
(handleTabsChange and various other logic)
useEffect(() => {
const offset = (page - 1) * limit;
setContactsPerPage(
entries[currentTab?.activeKey]?.slice(offset, offset + limit)
);
setLimit(viewData.limit);
}, [entries, currentTab?.activeKey, page, limit, data.limit]);
if (currentTab?.activeKey == null) {
return null;
}
return (
<div>
<Tabs
index={tabIndex}
onChange={handleTabsChange}
>
<TabList>
{tabs.map((item: string) => (
<Tab key={item}>{item}</Tab>
))}
</TabList>
<TabPanels>
{tabs.map((item: string) => (
<TabPanel key={item}>
... do some stuff ...
/>
))}
</TabPanel>
))}
</TabPanels>
</Tabs>
<Container>
<div className="w-full md:w-8/12">
<Pagination
totalEntries={entries[currentTab?.activeKey]?.length}
limit={data.limit}
page={page}
setPage={setPage}
/>
</div>
</Container>
</div>
);
}
Here is the test I am working on. Very simple. Check that myContacts has rendered.
import { render, screen } from "#testing-library/react";
import myContacts from "../myContacts";
test("render contacts", () => {
render(<myContacts />);
expect(screen.getByText(/component myContacts/i)).toBeInTheDocument();
});
However, I am getting the following error.
TypeError: Cannot read properties of undefined (reading 'limit')
23 | const { link, route, viewData } = props;
24 |
> 25 | const [limit, setLimit] = useState<number>(viewData.limit);
My initial thought was to do the following:
test("render contacts", () => {
const limit = 5;
render(<myContacts limit={limit} />);
expect(screen.getByText(/component myContacts/i)).toBeInTheDocument();
});
but this resulted in the same error.
I know this error is because 'limit' is not defined in the test, but I am at a loss on how to define and pass it in the test.
while building my react app for deployment, I am getting this error
TypeError: Cannot read property '0' of undefined
when I am rending on port3000 I did not see this error but only get it while building the app.
Can anyone assist to resolve this?
import { useState } from "react";
import styles from "./Tabs.module.css"
const Tabs = ({ children}) => {
const [activeTab, setActiveTab] = useState (children [0].props.label);
const handleClick =( e, newActiveTab ) => {
e.preventDefault();
setActiveTab(newActiveTab);
}
return (
<div>
<ul className= {styles.tabs}>
{children.map ((tab) => {
const label = tab.props.label;
return (
<li
className= {label == activeTab ? styles.current : ""}
key= {label}
>
<a href="#" onClick={(e) => handleClick (e, label)}>{label}
</a>
</li>
)
})}
</ul>
{children.map ((tabcontent1) => {
if (tabcontent1.props.label == activeTab)
return (
<div key= {tabcontent1.props.label} className= {styles.content}>{tabcontent1.props.children}
</div>
);
})}
</div>
);
}
export default Tabs ;
In next js, when you don't put export const getServerSideProps = () => {} in your page then that page is automatically subjected to static side rendering. On development mode, you may see a lightening symbol on bottom-right. Anyway you can read the docs on data-fetching on nextjs. However, your issue on this situation can be easily fixed by setting the children through useEffect.
// handle null on your active tab render function
const [activeTab, setActiveTab] = useState(null);
useEffect(() => {
if(children.length)
children[0].props.label
}, [children])
Another Code Sample:
*A simple change in code structure and the way you are trying to do. It's on react but kind of same in next as well *
import React from "react";
const Tabs = ({ tabsData }) => {
const [activeTabIndex, setActiveTabIndex] = React.useState(0);
const switchTabs = (index) => setActiveTabIndex(index);
return (
<div style={{ display: "flex", gap: 20, cursor: "pointer" }}>
{/* Here active tab is given a green color and non actives grey */}
{tabsData.map((x, i) => (
<div
key={i}
style={{ color: activeTabIndex === i ? "green" : "#bbb" }}
onClick={() => switchTabs(i)}
>
{x.label}
</div>
))}
{/* Show Active Tab Content */}
{tabsData[activeTabIndex].content}
</div>
);
};
export default function App() {
// You can place it inside tabs also in this case
// but lets say you have some states on this component
const tabsData = React.useMemo(() => {
return [
// content can be any component or React Element
{ label: "Profile", content: <p>Verify all Input</p> },
{ label: "Settings", content: <p>Settings Input</p> },
{ label: "Info", content: <p>INput info</p> }
];
}, []);
return (
<div className="App">
<Tabs tabsData={tabsData} />
</div>
);
}
and here is also a example sandbox https://codesandbox.io/s/serverless-night-ufqr5?file=/src/App.js:0-1219
UPDATED!
I'm creating a wrapper for dropdown menu and use it in several components so I need to make such a Menu generic. The problem is I do not understand how to pass external variables into such a component.
My component:
const SelectOptionsPaginated = ({
alignment, minWidth, width,
rowData,
column
}) => {
..........
const Menu = (props) => {
const {options} = props;
const dropdownContainer = useRef(null);
const [maxMenuHeight, setMaxMenuHeight] = useState(300)
const [dropDownStyle, setDropDownStyle] = useState({
position: "absolute",
minWidth: `${minWidth ? minWidth + "px" : "100%"}`,
maxWidth: `${width}px`,
maxHeight: `${maxMenuHeight}px`,
top: `32px`
})
const getDropdownPosition = (elem) => {
setDropDownStyle({
...dropDownStyle,
...getDropdownAlignment(elem, setMaxMenuHeight, gridId, options, true)
})
};
useEffect(() => {
const optionsList = dropdownContainer.current
if (!optionsList) return
getDropdownPosition(optionsList)
}, [options])
return (
<div
className="dropdown-container"
ref={dropdownContainer}
style={dropDownStyle}
>
<components.Menu {...props} >
{props.children}
</components.Menu>
</div>
)
}
............
return <AsyncPaginate
additional={defaultAdditional}
isMulti={isMulti}
value={value}
loadOptions={loadOptions}
onChange={handleChange}
escapeClearsValue
isClearable
styles={getStylesForSelectorEditor(width, minWidth, newAlignment)}
components={{Menu}}
/>
such variables as minWidth, width should be passed externally to Menu.
I tried something like:
...............
return <AsyncPaginate
additional={defaultAdditional}
isMulti={isMulti}
value={value}
loadOptions={loadOptions}
onChange={handleChange}
escapeClearsValue
isClearable
styles={getStylesForSelectorEditor(width, minWidth, newAlignment)}
// pseudocode
components={{<Menu width={100}/>}} or
components={{Menu(100)}}
/>
but it doesn't work.
I tried to google but didn't find clear information. I'm new in react so will appreciate any help.
Did you meant something like that?
const AsyncPaginate= (props) => {
const {components} = props;
return (
<>
{components}
</>
)
}
const Menu = () => {
return (
<>
something...
</>
)
}
const App = () => {
return (
<>
<AsyncPaginate components={<Menu />}></AsyncPaginate>
</>
)
}
I need to pass "notecards" (an array) down from "Notecard.js" to "LoadQuestions.js". Console log shows that it is passing, but when I use {notecards} within the "return" it errors as "undefined". Could you please take a look?
Notecard.js (without the imports):
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: 345,
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9
},
}));
export default function Notecard( {notecards} ) {
const classes = useStyles();
const next = () => {
console.log('Next Button Clicked')
};
const previous = () => {
console.log('Back Button Clicked')
};
const hint = () => {
console.log('Hint Button Clicked')
};
console.log({notecards});
return (
<Card className={classes.root}>
<div id="cardBody">
<CardHeader
title="Kate Trivia"
// subheader="Hint: In the 20th century"
/>
<CardContent>
<LoadQuestions notecards={notecards}/>
</CardContent>
</div>
</Card>
);
}
LoadQuestions.js (without imports)
const {useState} = React;
export default function LoadQuestions( {notecards} ) {
const [currentIndex, setCounter] = useState(0);
console.log({notecards});
return (
<div>
<Toggle
props={notecards}
render={({ on, toggle }) => (
<div onClick={toggle}>
{on ?
<h1>{props.notecards} hi</h1> :
<h1>{this.props[currentIndex].backSide}</h1>
}
</div>
)}
/>
<button onClick={() => {
console.log({notecards})
if (currentIndex < (this.props.length-1)) {
setCounter(currentIndex + 1);
} else {
alert('no more cards')
}
}}>Next Card
</button>
<button onClick={() => {
if (currentIndex > 0 ) {
setCounter(currentIndex -1);
} else {
alert('no previous cards')
}
}}>Previous Card
</button>
</div>
);
}
Thanks in advance!
That's all the details I have for you, but stack overflow really wants me to add more before it will submit. Sorry!
You should check if props exists, first time it renders the component it has no props so it shows undefined.
First i must say you destructured notecards out, so no need to use props.
If you want to use props you should change
({notecards}) to (props)
and if not you can directly use notecards since it is destructured
I suggest you two ways
adding question mark to check if exists
<h1>{props?.notecards} hi</h1>//in the case you want to use props
or
add the props in a if statement
<h1>{props.notecards?props.notecards:''} hi</h1> // if notecards is destructured remove the "props."
I have an object of arrays that I'm mapping over called featuredProj, and on alternate items, I want the CSS to conditionally render as not to repeat so much code. Using useState in the handleSide function I get the error too many rerenders. How can I solve this, or is there a better solution to rendering jsx while mapping over an array of objects.
const useStyles = makeStyles(() => ({
title: {
textAlign: (side) => (side ? "right" : "left"),
},
}));
const FeaturedProjects = () => {
const [side, setSide] = useState(true);
const classes = useStyles(side);
const handleSide = (project, index) => {
if (index === 0 || index % 2 === 0) {
// I tried setSide(false), setSide(prev => !prev)
return (
<Grid Container key={index}>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
);
} else {
return (
<Grid Container key={index}>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
);
}
};
return (
<Container>
{featuredProj.map((proj, ind) => (
<Reveal duration="2000" effect="fadeInUp">
{handleSide(proj, ind)}
</Reveal>
))}
</Container>
);
};
Thanks in advance for any assistance!
You cannot call setState() inside render method. setState() will trigger a re-rendering which calls render method again which leads to another setState().. you get the idea.
If you want it to work, you need to create separate component with the side props and pass it as an argument to your style hook.
const FeaturedProjects = () => {
const classes = useStyles(side);
return (
<Container>
{featuredProj.map((proj, ind) => (
<FeaturedProjectItem
key={index}
project={proj}
side={index === 0 || index % 2 === 0}
/>
))}
</Container>
);
};
const useStyles = makeStyles({
title: {
textAlign: (side) => (side ? "right" : "left"),
},
});
const FeaturedProjectItem = ({ side, project }) => {
const classes = useStyles(side);
return (
<Reveal duration="2000" effect="fadeInUp">
<Grid Container>
<Typography className={classes.title}>{project.title}</Typography>
</Grid>
</Reveal>
);
};