Project based on Typescript vs ReactJS.
This is render code :
return (
<div ref={this.myRef} style={this.state.myStyle} >
{this.state.sections.map((sectionsItem: AppI.SectionI) => {
if (this.state.activeSection === sectionsItem.name) {
console.log("TEST :", sectionsItem.elements );
sectionsItem.elements.map((element: React.ReactElement<any>, index: number) => {
return <span key={index} >{element}</span>;
});
}
})}
</div>
);
In debugger I can see that 'elements' are not empty but it doesn't render in html.
Any suggestion ?!
You need an extra return statement:
Change: sectionsItem.elements.map to return sectionsItem.elements.map:
Your inner .map returns elements but the outer .map has no return statement:
return (
<div ref={this.myRef} style={this.state.myStyle} >
{this.state.sections.map((sectionsItem: AppI.SectionI) => {
if (this.state.activeSection === sectionsItem.name) {
console.log("TEST :", sectionsItem.elements );
return sectionsItem.elements.map((element: React.ReactElement<any>, index: number) => {
return <span key={index} >{element}</span>;
});
}
})}
</div>
);
Related
Intro -
I'm trying to show JSON data in a Accordion. So I used react-sanfona (github) to build that. I'm trying to call getComponent function recursively so that I can check if it is an array or object if it is array I'm calling same function. better show you the pogress so far.
Problem - I'm getting [object Object] at the second level even I call the getComponent recursively
Edit on codesandbox
Problem was that you didn't return anything when dealing with object
So this part
Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
should be
return Object.keys(record).map((key, index) => {
console.log(44);
return (
<AccordionItem className="ml-5" title={`1 - ${index}`} expanded>
{getComponent(record[key])}
</AccordionItem>
);
});
I added a default expanded property and now it displays all data.
Check
this sandbox
Hy, I don't know exactly what you want to display but here is a version working.
import { Accordion, AccordionItem } from "react-sanfona";
import "./styles.css";
const datalist = [
{
id: 3423235234,
name: "John",
address: [
{
first: "city1",
second: "city2"
}
]
}
];
export default function App() {
function getComponent(record) {
if (Array.isArray(record)) {
return record.map((b, index) => (
<AccordionItem className="ml-5" title={`${index}`} key={index}>
{getComponent(b)}
</AccordionItem>
));
}
if (typeof record === "object") {
return (
<div>
{Object.keys(record).map((key, index) => {
return (
<AccordionItem
className="ml-5"
title={`1 - ${index}`}
expanded
key={index}
>
{getComponent(record[key])}
</AccordionItem>
);
})}
</div>
);
}
if (typeof record === "string" || typeof record === "number") {
console.log("string or number: ", record);
return <AccordionItem className="ml-5" title={`2 - ${record}`} />;
}
return (
<AccordionItem className="ml-5" title={`3 - ${record.toString()}`} />
);
}
return (
<div className="App">
<div className="px-7">
<Accordion>{getComponent(datalist)}</Accordion>
</div>
</div>
);
}
The package you're using raise many errors in the console (with no configuration). Did you check the material ui's accordions ? https://material-ui.com/components/accordion/
This is working code, which might help you
Here, initially root folder will be hiding all children once click on root -> its direct children will be displayed/hidden and so on
//explorer is the json coming from parent component
import React, { useState } from "react";
function Accodian({ explorer }) {
const [expand, setExpand] = useState(false);
if (explorer.children) {
return (
<div>
{/* {explorer.children ? ( */}
<>
<div onClick={() => setExpand((prevState) => !prevState)}>
{explorer.name}
</div>
{expand ? (
<>
{explorer.children.map((child) => {
// return <div key={child.name} style = {{paddingLeft: '20px'}}>{child.name}</div>;
return <Accodian explorer={child} key={child.name} />;
})}
</>
) : null}
</>
{/* ) : null} */}
</div>
);
} else {
return <div style={{ paddingLeft: "20px" }}>{explorer.name}</div>;
}
}
export default Accodian;
Check this sandbox : https://codesandbox.io/s/js-recursion-accordian-v03y5q?file=/src/components/Accordian.js:0-823/
I have an array of objects that contains any errors that are on the site. I have created a function that maps over the errors and return an error message and link to the page with the error on it:
const buildErrorLink = (pge, field) => {
return (
validationErrors &&
validationErrors.map(err => (
<>
<p className="error">{err.message}</p>
<h5 className="error-field-name">
{pge ? (
<a
href="#"
onClick={() => goToStep(pge, field, true)}
className="review-page-error"
>
{err.fieldName}
</a>
) : (
<>{err.fieldName}</>
)}
</h5>
</>
))
);
};
The goToStep() function just takes in a page and which field it is and that part works great.
The issue is in this function:
const validationError = () => {
if (validationErrors) {
if (validationErrors.filter(err => err.fieldName === 'name')) {
return buildErrorLink(0, 'name');
}
if (
validationErrors.filter(
err => err.fieldName === 'state'
)
) {
return buildErrorLink(1, 'state');
}
if (
validationErrors.filter(
err => err.fieldName === 'city'
)
) {
return buildErrorLink(1, 'city');
}
}
return buildErrorLink();
};
I should clarify that this is how I am returning validationError:
return (
<div className="error">{validationError()}</div>
)
That displays all error messages but stops on the first one in the validationError function and passes that page to all the links. So my question is how to pass the correct page number to each link? Thanks.
So the issue was I was mapping in the wrong place. I should have been mapping in the validationError function. My code ended up looking like this:
const buildErrorLink = (pge, fieldName, errMsg) => {
return (
<>
<p className="error">{errMsg}</p>
<h5 className="error-field-name">
{pge ? (
<a
href="#"
onClick={() => goToStep([pge], null, true)}
className="error"
>
{fieldName}
</a>
) : (
<>{fieldName}</>
)}
</h5>
</>
);
};
const validationError = () => {
return (
validationErrors &&
validationErrors.map(err => {
switch (err.fieldName) {
case 'Name':
return buildErrorLink(0, err.fieldName, err.message);
case 'State':
return buildErrorLink(1, err.fieldName, err.message);
case 'City':
return buildErrorLink(1, err.fieldName, err.message);
default:
return buildErrorLink();
}
})
);
};
I'm trying to map over a map in JSX but the second inner map doesn't render why?
{food.map((item: ItemsShape, index: number) => {
return (
<div key={`${index}`}>
<div>{item.Quantity}</div>
<div>{item.Name}</div>
{item.Options && <div>{JSON.stringify(item.Options)}</div>} // <= this shows al the options
{item.Options &&
item.Options.map((option: any) => {
<div>OPTION {option.Name}</div>;
})} // <= This doesnt even render why?
</div>
);
})}
You missed return
{food.map((item: ItemsShape, index: number) => {
return (
<div key={`${index}`}>
<div>{item.Quantity}</div>
<div>{item.Name}</div>
{item.Options && <div>{JSON.stringify(item.Options)}</div>} // <= this shows al the options
{item.Options &&
item.Options.map((option: any) => {
return (<div>OPTION {option.Name}</div>);
})}
</div>
);
})}
I am trying to loop through object of objects and using map to list the item and I am getting this object from redux. I can console.log the value but jsx returns nothing. I tried removing {} and return and using () only but still it is not rendering anything.
My posts object looks like
posts = { 1: {id: 1, title: "Hello world"} }
Component.js
renderList(){
const { posts } = this.props;
Object.keys(posts).map(key => {
console.log(`${key} and ${posts[key].title}`);
return (
<li key={key} className="list-group-item">
{posts[key].title}
</li>
);
});
}
render(){
return (
<div>
<h2>Posts</h2>
<ul className="list-group">{this.renderList()}</ul>
</div>
);
}
I can't figure out what I am doing wrong.
Your renderList method does not have a return statement.
Depending on if your version of React you can either return an array here or you need to wrap it in a div (or in this case put the ul into it).
renderListOnReact16(){
const { posts } = this.props;
return Object.keys(posts).map(key => {
console.log(`${key} and ${posts[key].title}`);
return (
<li key={key} className="list-group-item">
{posts[key].title}
</li>
);
});
}
renderOnReact16(){
return (
<div>
<h2>Posts</h2>
<ul className="list-group">{this.renderList()}</ul>
</div>
);
}
renderListOnReact15(){
const { posts } = this.props;
return (
<ul className="list-group">
{Object.keys(posts).map(key => {
console.log(`${key} and ${posts[key].title}`);
return (
<li key={key} className="list-group-item">
{posts[key].title}
</li>
);
})}
);
}
renderOnReact16(){
return (
<div>
<h2>Posts</h2>
{this.renderList()}
</div>
);
}
I'm trying to render an array of messages but would want it to render differently by class given a condition my code looks like this:
render() {
return (
<div>
{this.props.messages.map((m, index) => (
//if m.id === 1 render this:
<p className={someClass1}>Hello, {m.message}!</p>
//else render this:
<p className={someClass2}>Hi, {m.message}!</p>
))}
</div>);
}
you can easily add logic to your map. you just need the contents to not be an inline return of a react component.
render() {
return (
<div>
{this.props.messages.map((m, index) => {
if (m.id === 1){
return <p className={someClass1}>Hello, {m.message}!</p>
}
return <p className={someClass2}>Hi, {m.message}!</p>
})}
</div>
);
}
You can also do the same thing with a forEach outside of the return on your render like so
render() {
const elems = [];
this.props.messages.forEach( (m, index) => {
if (m.id === 1) {
elems.push(<p className={someClass1}>Hello, {m.message}!</p>);
} else {
elems.push(<p className={someClass2}>Hi, {m.message}!</p>);
}
return (
<div>
{elems}
</div>
);
}