ReactJS not able to print nested values in rest api response - javascript

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>

Related

Populating the list with array of items

I have an array of items, and I have accessed one of the item's property to divide the items into two separate arrays. But when I try to populate the created array in the component of the DOM, gives an error
Uncaught Error: Objects are not valid as a React child (found: object with keys {text, key, completedItem}). If you meant to render a collection of children, use an array instead.
The code is as follows:
render() {
let { items } = this.props;
let listItems = items.map( (item) => {
return (
<TodoItem text={item.text}
key={item.key}
ID={item.key}
isComplete={item.completedItem}
onDelete={this.props.onDelete}
onEdit={this.props.onEdit}
handleComplete={this.props.handleComplete} />
);
});
const revArray = listItems.reverse();
console.log("Reversed Array : ", revArray);
const completedItems = items.filter((item) => {
return ( item.completedItem == true );
});
console.log("Items Completed Array : ", completedItems);
const filterCompleted = items.filter((item) => {
return ( item.completedItem == false );
});
console.log("Items not Completed Array : ", completedItems);
const resultArrItems = [ ...filterCompleted, ...completedItems];
console.log("Final Array : ", resultArrItems);
return (
<div className="card card-body mt-5">
<ul className="list-group my-3">
<h3 className="text-capitalize text-center"> todo list </h3>
<div>{resultArrItems}</div>
</ul>
</div>
);
}
I get the resultArrItems as of the exact order I need the array elements to be, when I view the array in the console, but the problem is I can't populate the component in the DOM with the resultArrItems. The error that I get is mentioned above. Is there any solution for this ...? and also what is the reason for this behavior ...?
You only can represent elements in render or return with JSX syntax. Currently you are trying to display an array - resultArrItems. You need to use .map() and return inside a valid JSX element for each iteration.
You can try the following:
return <div className="card card-body mt-5">
<ul className="list-group my-3">
<h3 className="text-capitalize text-center"> todo list </h3>
<div>
{
resultArrItems.map((e, i) => <div key={i}>
{i} {/* here you can access also each elements properties */}
</div>)
}
</div>
</ul>
</div>
I hope this helps!

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.

Running into issues mapping information with React

I've been stuck on this problem for the longest time and haven't been able to make any headway on why I'm not able to map the values to the screen.
For testing purposes, when the code beneath the renderSurvey() console.log is commented out; the console will highlight two objects (one as an empty object and the second one as an array, after the data, is loaded from the database (picture).)
My running hypothesis is that the empty first object is causing the issues but I'm still stumped.
class SurveyList extends Component {
componentDidMount(){
this.props.fetchSurveys();
}
renderSurveys(){
console.log(this.props.surveys);
return this.props.surveys.map(survey => {
return (
<div className='card darken-1' key = {survey._id}>
<div className='card-content'>
<span className='card-title'>{survey.title}</span>
<p>
{survey.body}
</p>
<p className='right'>
Sent On: {new Date(survey.dateSent).toLocaleDateString()}
</p>
</div>
<div className='card-action'>
<a>Yes: {survey.yes}</a>
<a>No: {survey.no}</a>
</div>
</div>
)
})
}
render(){
return(
<div>
{this.renderSurveys()}
</div>
)
}
}
function mapStateToProps({ surveys }) {
return { surveys };
}
export default connect(mapStateToProps, { fetchSurveys })(SurveyList);
Try this:
render(){
if(this.props.surveys && this.props.surveys.length){
return(
<div>
{this.renderSurveys()}
</div>
)
}else{
return(
<div></div>
)
}
}
It will check if props.surveys is received and fulfilled then try run renderSurveys() in render().

React: Better way of mapping this data

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!

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