React: Better way of mapping this data - javascript

So this is a continuation of previous questions. I just started using react and I've managed to fetch a xml file, convert it to json and then loop through the data. But I think there should be a better way to go through what I've done as I'm using a function I found on another SO answer and then used a map() within it.
Anyways here's the breakdown of my code.
Fetching the xml and converting to json:
componentDidMount() {
axios.get('https://gist.githubusercontent.com/eMatsiyana/e315b60a2930bb79e869b37a6ddf8ef1/raw/10c057b39a4dccbe39d3151be78c686dcd1101aa/guestlist.xml')
.then(res => {
const xml = XMLMapping.load(res.data);
var guests = XMLMapping.tojson(xml);
this.setState({guests: guests});
});
}
The results of the json in console:
Object{
dataset{
record{
0{
company{
$t: "Skippad"
}
first_name{
$t: "Keith"
}
last_name{
$t: "Cook"
}
}
1{
company{
$t: "Skippad"
}
first_name{
$t: "Keith"
}
last_name{
$t: "Cook"
}
}
}
}
}
I'm using this function to map the object and then using a map() within it:
function mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
});
}
This is what the final mapping of the data looks like:
{mapObject(this.state.guests, (key, value) => {
return <div key={key}>
{value.record
.filter(
(item,index) => {
return (
item.first_name.$t.toLowerCase().includes(this.state.search.toLowerCase())
//item.last_name.$t.toLowerCase().includes(this.state.search.toLowerCase())
//item.company.$t.toLowerCase().includes(this.state.search.toLowerCase())
)
}
)
.map((item,index) => {
return <div className="columns is-mobile" key={index}>
<div className="column" key={index}>{item.first_name.$t} {item.last_name.$t} <span class="is-hidden-tablet"><br />{item.company.$t}</span></div>
<div className="column is-hidden-mobile" >{item.company.$t}</div>
<div className="column is-hidden-mobile">
<EmailFormdisplay guestid={index} />
</div>
<div className="column is-hidden-mobile">
<PhoneFormdisplay guestid={index} />
</div>
<div className="column is-hidden-tablet is-one-third-mobile">
<Dropdown>
<DropdownTrigger></DropdownTrigger>
<DropdownContent>
<div className="columns">
<div className="column">
<EmailFormdisplay guestid={index} />
</div>
<div className="column">
<PhoneFormdisplay guestid={index} />
</div>
</div>
</DropdownContent>
</Dropdown>
</div>
</div>;
})}
</div>
})}
Is there a better way of doing this without using mapObject() and a map() within it?
Any kind of feedback or advice would be greatly appreciated!

Related

ReactJS not able to print nested values in rest api response

This is first time I am writing a React app and have create a sample app using create-react-app. I am calling a rest api using axios and it returns me json below which is nested. My component display the first level attributes but not nested one please help understand what I am doing wrong.
<Card type="inner" title="Social">
{social.dataall.map((social1,i) => (
<div key={i} class="card">
<div class="card-body">
<h5 class="card-title">Name: {social1.FullName}</h5>
{social1.Member1.map(function (member11, j) {
return <div key={j}> <h5>member11</h5></div>
{member11.User2.map(function (usr2, k) {
return <div key={k}> <h5>Type: {usr2.firstName}</h5></div>
})}
})}
</div>
</div>
))}
</Card>
JSON to parse and display:
{"app":{"dataall":[{"FullName":"Payment","DisplayName":"Payment","Id":"3366d5e59","Member1":[{"User2":[{"userId":"331322934","firstName":"fName1","lastName":"lName1"}],"Role3":[{"roleName":"Business"}]},{"User2":[{"userId":"331322934","firstName":"fName","lastName":"lName"}],"Role3":[{"roleName":"Owner"}]}]}]}}
Output:
Name: Payment
member11
member11
Not able to print the value for Type: {usr2.firstName}
You are closing the <div> element here:
return <div key={j}> <h5>member11</h5></div>
// ^^^^^^
That means the following code ({member11.User2...) is not part of the JSX element. It simply follows the return statement. Code after a returns statement is not executed:
(function() {
console.log("shown");
return;
console.log("not shown");
}());
You didn't explain how you want the UI to be structured but a simple solution would be to move the mapping inside the <div> element.
<div class="card-body">
<h5 class="card-title">Name: {social1.FullName}</h5>
{
social1.Member1.map(function (member11, j) {
return (
<div key={j}>
<h5>member11</h5>
{
member11.User2.map(function (usr2, k) {
return <div key={k}> <h5>Type: {usr2.firstName}</h5></div>
})
}
</div>
);
})
}
</div>

iterating through an array of objects and displaying the items [REACT JS]

I'm trying to iterate through an array of objects, displaying the results inside divs but something is not working as intended. When I console log it seems to retrieve the data and show it.
const example =
{
"example": [
{
"test": "test",
"img": "img.png",
"song": "song title"
},
{
"test": "test2",
"img": "img.png2",
"song": "song title2"
}
]
}
const renderData= () => {
example.example.forEach(function (arrayItem) {
const test= arrayItem.test
const img= arrayItem.img
const song= arrayItem.song
return (
<div className="test">
<div className="test">
<div className="test">
<img
src={img}
alt="sunil"
/>
</div>
<div className="test">
{test}
<span className="test">
</span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};
return (
<div
{renderData()}
</div>
);
}
nothing really shows up, but when i do:
example.example.forEach(function (arrayItem) {
var x = arrayItem.test+ arrayItem.img+ arrayItem.song;
console.log(x);
});
it works and consoles the right info.
Can anyone spot the mistake or help out?
Please ignore the naming convention.
You need return array of JSX.Element from renderData. In your case you return undefined. Return a new array of JSX.Element with map instead forEach, which returns nothing.
const renderData = () => {
return example.example.map((arrayItem, i) => {
const test = arrayItem.test;
const img = arrayItem.img;
const song = arrayItem.song;
return (
<div key={i} className="test">
<div className="test">
<div className="test">
<img src={img} alt="sunil" />
</div>
<div className="test">
{test}
<span className="test"></span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};

How to pass ref to component in React?

I am using a library called react-swipe (not especially relevant), which exposes next() and prev() methods on the instance, which I am accessing through a ref.
When I have the ReactSwipe component in my main App.js file this works perfectly well, e.g.:
_handlePrev() {
this.reactSwipe.prev()
}
_handleNext() {
this.reactSwipe.next()
}
render() {
let singlePlanets
singlePlanets = this.state.planetData.map(data => {
return (
<div className="single-planet" key={data.id}>
<div className="image">
<img src={emptyPlanet} alt={data.name} />
</div>
<h2>{data.name}</h2>
<div className="extract" dangerouslySetInnerHTML={{ __html: data.extract }} />
</div>
)
})
return (
<div className="app-container">
<TitleBar />
<ReactSwipe ref={reactSwipe => this.reactSwipe = reactSwipe} className="content" key={singlePlanets.length}>
{singlePlanets}
</ReactSwipe>
<MenuBar handleNext={this._handleNext.bind(this)} handlePrev={this._handlePrev.bind(this)} />
</div>
)
}
But what I'm trying to do is separate out the ReactSwipe and planetData mapping logic into its own component (code below), however when I do this (by trying to pass the ref through as a prop) I always get the error this.reactSwipe.prev() (or .next()) is not a function, no matter what I try. I'm wondering - what is the correct way to go about this?
This what I have in my return in App.js:
<PlanetInfo planetData={this.state.planetData} swipeRef={reactSwipe => this.reactSwipe = reactSwipe} />
and in PlanetInfo component:
return (
<ReactSwipe ref={this.swipeRef} className="content" key={singlePlanets.length}>
{singlePlanets}
</ReactSwipe>
)
Replace ref={this.swipeRef} with ref={this.props.swipeRef} in PlanetInfo component.

react map returned error of Objects are not valid as a React child

I failed to render jxs using map of lodash. I have this in my render method
return (
<div>
{map(groupedByMonths, (obj, index) =>
<div className="panel">
<div className="panel-heading">
{obj}
</div>
<div>{obj.price}</div>
</div>)}
</div>
)
But I got error of Objects are not valid as a React child The groupedByMonths structure look like this
{
"April": [
{
"price": 0,
},
{
"price": 12
},
{
"price": 200
}
]
}
Don't know how to do this with lodash map, i can suggest this:
{Object.keys(groupedByMonths).map((obj, index) =>
<div className="panel">
<div className="panel-heading">
{obj}
</div>
{groupedByMonths[obj].map((el, i) => <div key={i}> {el.price} </div>)}
</div>)
}
When we use Object.keys(groupedByMonths) it will return an array that will contain all the keys, and we can use map on that. To print the key use {obj} and to print the values use {groupedByMonths[obj]}.

React - how to map nested object values?

I am just trying to map nested values inside of a state object. The data structure looks like so:
I want to map each milestone name and then all tasks inside of that milestone. Right now I am trying to do so with nested map functions but I am not sure if I can do this.
The render method looks like so:
render() {
return(
<div>
{Object.keys(this.state.dataGoal).map( key => {
return <div key={key}>>
<header className="header">
<h1>{this.state.dataGoal[key].name}</h1>
</header>
<Wave />
<main className="content">
<p>{this.state.dataGoal[key].description}</p>
{Object.keys(this.state.dataGoal[key].milestones).map( (milestone, innerIndex) => {
return <div key={milestone}>
{milestone}
<p>Index: {innerIndex}</p>
</div>
})}
</main>
</div>
})}
</div>
);
}
I think that I could somehow achieve that result by passing the inner index to this line of code: {Object.keys(this.state.dataGoal[key].milestones) so it would look like: {Object.keys(this.state.dataGoal[key].milestones[innerIndex]).
But I am not sure how to pass the innerIndex up. I have also tried to get the milestone name by {milestone.name} but that doesn't work either. I guess that's because I have to specify the key.
Does anybody have an idea? Or should I map the whole object in a totally different way?
Glad for any help,
Jakub
You can use nested maps to map over the milestones and then the tasks array:
render() {
return (
<div>
{Object.keys(this.state.dataGoal.milestones).map((milestone) => {
return (
<div>
{this.state.dataGoal.milestones[milestone].tasks.map((task, idx) => {
return (
//whatever you wish to do with the task item
)
})}
</div>
)
})}
</div>
)
}
What you want is flatMap. flatMap takes an array and a function that will be applied to each element in the array, which you can use to (for example) access properties inside each object in the array. It then returns a new array with the returned values from its lambda:
function flatMap(arr, lambda) {
return Array.prototype.concat.apply([], arr.map(lambda))
}
In our case, we don't have an array, we have an object so we can't use flatMap directly. We can convert the object to an array of its properties' values with Object.values and then make a function that accesses the object with the passed key:
function tasksFromDataGoal(key) {
return flatMap(Object.values(dataGoal[key].milestones), milestone => milestone.tasks)
}
Working example:
function flatMap(arr, lambda) {
return Array.prototype.concat.apply([], arr.map(lambda))
}
function tasksFromDataGoal(key) {
return flatMap(Object.values(dataGoal[key].milestones), milestone => milestone.tasks)
}
const dataGoal = { 123: { milestones: { milestone1: { tasks: ['a', 'b'] }, milestone2: { tasks: ['c', 'd'] } } } }
alert(tasksFromDataGoal('123'))
Author of this implementation of flatMap: https://gist.github.com/samgiles/762ee337dff48623e729
Managed to refactor the render method:
render() {
return(
<div>
{Object.keys(this.state.dataGoal).map( (key, index) => {
const newDataGoal = this.state.dataGoal[key].milestones;
return <div key={key}>
<header className="header">
<h1>{this.state.dataGoal[key].name}</h1>
</header>
<Wave />
<main className="content">
<p>{this.state.dataGoal[key].description}</p><br /><br />
{Object.keys(this.state.dataGoal[key].milestones).map( (milestoneKey) => {
const milestonesData = this.state.dataGoal[key].milestones[milestoneKey];
return <div className="milestone-wrap" key={milestoneKey}>
<label className="milestone-label">{milestonesData.name}</label>
{Object.keys(milestonesData.tasks).map( (taskKey) => {
return <div className="task clearfix" key={taskKey}>
<input
className="checkbox-rounded"
name="task"
type="checkbox"
checked={milestonesData.tasks[taskKey].done}
onChange={(e) => this.handleInputChange(e, key, taskKey)} />
<div className="task-content">
<p className="task-name">{milestonesData.tasks[taskKey].name}</p>
<p className="task-date">{milestonesData.tasks[taskKey].finishDate}</p>
</div>
</div>
})}
</div>
})}
</main>
</div>
})}
</div>
);
}

Categories

Resources