How to map a nested array in react - reactjs

Ok. I am super lost here. I am attempting to return an a value from a nested array of objects. Here is what the database looks like.
Each object returns an array of staff as seen in this console log.
I cannot for the life of me display both names out of the array. I used the .flatMap() function to extract the data from the array, but when I go to render it in the component only the first name shows. Here is my codebase. Right now I am just console logging the data using .flatMap()
import React, { Fragment, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Table } from 'react-bootstrap';
import Moment from 'react-moment';
import Button from '../components/Button';
import ActivitySummary from '../components/ActivitySummary';
import { projectsInfoDetails } from '../actions/projectInfoActions';
import { projectContacts } from '../actions/projectContactActions';
import { projectStaff } from '../actions/accountableStaffActions';
import SectionHeader from '../components/SectionHeader';
import Loader from '../components/Loader';
import Message from '../components/Message';
const ProjectScreen = ({ match }) => {
const dispatch = useDispatch();
const projectInfoDetails = useSelector(state => state.projectInfoDetails);
const { loading, error, projects } = projectInfoDetails;
const contactDetails = useSelector(state => state.projectContactDetails);
const { projectContact } = contactDetails;
const projectAccountableStaff = useSelector(state => state.projectStaff);
const { accountableProjectStaff } = projectAccountableStaff;
useEffect(() => {
dispatch(projectsInfoDetails(match.params.id));
dispatch(projectContacts(match.params.id));
dispatch(projectStaff(match.params.id));
}, [dispatch, match]);
const lastName = projectContact.map(l => l.contact.lastName);
const firstName = projectContact.map(f => f.contact.firstName);
const accountableStaffLastNames = accountableProjectStaff.flatMap(
({ accountableStaff }) => accountableStaff.map(data => data.lastName)
);
console.log(accountableStaffLastNames);
return (
<Fragment>
<div>
<SectionHeader sectionName='Project' />
<Button buttonName='Edit Project' />
</div>
{loading ? (
<Loader />
) : error ? (
<Message variant='danger'>{error}</Message>
) : (
<div style={{ backgroundColor: '#F8F8F8' }}>
<Table className='borderless'>
<tbody>
<tr>
<td>
<strong>Name: </strong>
{projects.name}
</td>
<td>
<strong>Status: </strong>
{projects.status}
</td>
</tr>
<tr>
<td>
<strong>State: </strong>
{projects.state}
</td>
<td>
<strong>County: </strong>
{projects.county}
</td>
</tr>
<tr>
<td>
<strong>Congressional District: </strong>
{projects.district}
</td>
<td>
<strong>Type: </strong>
{projects.type}
</td>
</tr>
<tr>
<td>
<strong>Funding Source: </strong>
<br />
{`${projects.fundingSource} ${projects.fundingSourceName}`}
</td>
<td>
<strong>Funding Source Goal: </strong>
<br />
{projects.fundingSourceGoal}
</td>
<td>
<strong>Start Date: </strong>
<br />
<Moment format='MM/DD/YYYY'>{projects.startDate}</Moment>
</td>
<td>
<strong>End Date: </strong>
<br />
{projects.endDate === null ? (
''
) : (
<Moment format='MM/DD/YYYY'>{projects.endDate}</Moment>
)}
</td>
<td>
<strong>Funding Percent: </strong>
<br />
{projects.fundingPercent}
</td>
</tr>
<tr>
<td>
<strong>Contact: </strong>
{firstName} {lastName}
</td>
</tr>
<tr>
<td>
<strong>Start Date: </strong>
<Moment format='MM/DD/YYYY'>
{projects.projectStartDate}
</Moment>
</td>
<td>
<strong>End Date: </strong>
{projects.projectEndDate === null ? (
''
) : (
<Moment format='MM/DD/YYYY'>
{projects.projectEndDate}
</Moment>
)}
</td>
</tr>
<tr>
<td colSpan='5'>
<strong>Goals and Objectives: </strong>
{projects.goalsAndObjectives}
</td>
</tr>
<tr>
<td colSpan='5'>
<strong>Success Description: </strong>
{projects.successDescription}
</td>
</tr>
<tr>
<td>
<strong>Accountable Staff: </strong>
</td>
</tr>
<tr>
<td>
<strong>Project ID: </strong>
{projects.projectId}
</td>
</tr>
</tbody>
</Table>
</div>
)}
<ActivitySummary />
</Fragment>
);
};
export default ProjectScreen;

Related

Message prints in every Dynamic Accordion in ReactJs

I have a dynamic Accordion in ReactJs. I am getting the message from my backend. but it's printing in every Accordion. I'm sharing the code
import React, { useState, useEffect } from "react";
import ApplicantDash from "./ApplicantDash";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from "#material-ui/core";
import * as FcIcons from "react-icons/fc";
import ApplicantService from "../services/ApplicantService";
export default function AvailJobs() {
const [aplcntEmail, setAplcntEmail] = useState("aman#gmail.com"); //change to aplcntemail
const [isShow, setIsShow] = useState(false);
const [msg, setMsg] = useState([""]);
const [job, setJob] = useState([
{
jobTitle: "",
dateOfPosting: Date,
lastDateToApply: new Date().toLocaleDateString([], {
year: "numeric",
month: "long",
day: "numeric",
}),
preferableSkills: [],
requiredExp: 0,
recruiterEmail: "",
companyName: "",
companyAddress: "",
},
]);
useEffect(() => {
const data = ApplicantService.getAllJobs()
.then((response) => {
console.log(response.data);
setJob(response.data);
})
.catch((error) => {
alert(error.response.data);
});
}, []);
const onApplyButton = (item,key) => {
const data2 = ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setIsShow(true);
setMsg(response.data)
})
.catch((error) => {
setIsShow(true);
setMsg(error.response.data);
});
};
return (
<div>
<ApplicantDash />
<div className="container bg-light">
<div className="card-bodies">
<section className="mb-4">
<h2 className="h1-responsive font-weight-bold text-center my-4">
All Available jobs
</h2>
</section>
{job.map((item, key) => (
<>
<Accordion key={key}>
<AccordionSummary
expandIcon={<FcIcons.FcExpand />}
aria-controls="panel1a-content"
id="panel1a-header"
className="Accordian"
>
<Typography>
<div className="d-flex p-1 justify-content-evenly">
<div className="p-1">
<b> Job: </b> {item.jobTitle}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Company: </b> {item.companyName}
</div>
<div className="p-2"></div>
<div className="p-1">
<b> Last Date: </b> {item.lastDateToApply}
</div>
</div>
</Typography>
</AccordionSummary>
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
<tr>
<td>JOB TITLE</td>
<td>:</td>
<td>
<b>{item.jobTitle}</b>
</td>
</tr>
<tr>
<td>Company</td>
<td>:</td>
<td>
<b>{item.companyName}</b>
</td>
</tr>
<tr>
<td>Address</td>
<td>:</td>
<td>
<b>{item.companyAddress}</b>
</td>
</tr>
<tr>
<td>Last Date to Apply</td>
<td>:</td>
<td>
<b>{item.lastDateToApply}</b>
</td>
</tr>
<tr>
<td>Experience</td>
<td>:</td>
<td>
<b>{item.requiredExp}</b>
</td>
</tr>
<tr>
<td> Skills </td>
<td>:</td>
<td>
<table className="table table-condensed w-auto table-borderless table-hover">
{item.preferableSkills.map((S, index1) => {
return (
<tbody key={index1}>
<td scope="col">
{index1 + 1}.<b>{S}</b>
</td>
</tbody>
);
})}
</table>
</td>
</tr>
<tr>
<td></td>
<td></td>
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item,key)}
>
Apply for the job{" "}
</button>
</td>
</tr>
</tbody>
{isShow && <>
{msg}
</>}
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
</>
))}
</div>
</div>
</div>
);
}
Now when I click on Apply for this job button. The message I get from backend prints only to Active accordion
Here some pictures which might help.
enter image description here
As you can see the response from backend is prints in the both of the accordion
Issue
The issue here is that you've a single boolean isShow state and a single msg state, and all the accordion detail sections use the same single isShow state to conditionally render the msg state.
Solution
A simple solution would be to store the id, or title, or index, of the accordion to show the message of.
Example:
export default function AvailJobs() {
...
const [isShow, setIsShow] = useState({}); // <-- initially empty object
...
const onApplyButton = (item, key) => {
ApplicantService.applyForJob(aplcntEmail, item)
.then((response) => {
console.log(response.data);
setMsg(response.data);
})
.catch((error) => {
setMsg(error.response.data);
})
.finally(() => {
setIsShow(show => ({
...show,
[key]: true // <-- set true the specific key
}));
});
};
return (
<div>
...
{job.map((item, key) => (
<Accordion key={key}>
...
<AccordionDetails>
<Typography>
<div className="container">
<table class="table table-borderless">
<tbody>
...
<tr>
...
<td>
<button
type="button"
class="btn btn-primary"
onClick={() => onApplyButton(item, key)}
>
Apply for the job
</button>
</td>
</tr>
</tbody>
{isShow[key] && <>{msg}</>} // <-- check if isShow[key] is truthy
</table>
</div>
</Typography>
</AccordionDetails>
</Accordion>
))}
...
</div>
);
}

React hook form's FieldArray crashes app when deleting item

I made a dynamic input table component with react-hook-form. You are able to add, remove and edit fields in the table. Here's how it looks like
import {useFieldArray, useFormContext} from "react-hook-form";
import {cloneElement} from "react";
import {IoMdAdd, IoMdRemoveCircle} from "react-icons/io";
interface TableData {
tableName:string,
inputFields: {
title: string,
name: string,
inputComponent: any,
}[],
inputBlueprint: object,
min?: number
};
const InputTable = ({tableName, inputFields, inputBlueprint, min}: TableData) => {
const {fields, remove, append} = useFieldArray({name: tableName});
const {register, formState: {errors}} = useFormContext();
return (
<table className="table-auto border-collapse block m-auto w-fit max-w-xs max-h-48 overflow-auto sm:max-w-none my-3">
<thead className="text-center">
<tr>
{inputFields.map((input) => (
<td className="border-2 border-gray-400 px-5" key={input.title}>{input.title}</td>
))}
</tr>
</thead>
<tbody>
{fields.map((field, index) => (
<tr key={field.id}>
{inputFields.map((input) => (
<td key={input.title} className="border-gray-400 border-2 p-0">
{cloneElement(input.inputComponent, {
className: "bg-transparent outline-none block w-full focus:bg-gray-400 dark:focus:bg-gray-500 p-1",
...register(`${tableName}.${index}.${input.name}` as const)
})}
{errors[tableName]?.[index][input.name] &&
<p className="bg-red-400 p-1">
{errors[tableName][index][input.name]?.message}
</p>
}
</td>
))}
{(min === undefined || min <= index) &&
<td onClick={() => remove(index)}><IoMdRemoveCircle className="text-red-600 text-2xl"/></td>
}
</tr>
))}
<tr>
<td onClick={() => append(inputBlueprint)} className="bg-green-500 border-gray-400 border-2"
colSpan={inputFields.length}>
<IoMdAdd className="m-auto"/>
</td>
</tr>
{errors[tableName] &&
<tr>
<td className="max-w-fit text-center">
{errors[tableName].message}
</td>
</tr>}
</tbody>
</table>
)
}
export default InputTable
Now, whenever I have only one element in the array it works just fine but when having more than one it crashes whenever an input field is emptied through typing. I don't really know how to explain it clearly so here's a gif showing it

Elements not rendering in react

I am doing a fetch and storing the result in my state "data", then i am sending this "data" to the function "Showingmovies" , there i am using table tag to align my layout . But the elements are not being rendered from the "Showingmovies" function, i have found that the props are being succesfully passed to the function still it is not rendering.
Below is my complete code -
import React, { Component } from 'react'
function Showingmovies(props){
console.log("in showingmovies",props.mov)
return(
<table>
<tbody>
{props.mov.results.map((mv) =>{
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>
})}
</tbody>
</table>
)}
export default class movie extends Component {
constructor(){
super()
this.state={
data: [],
}
}
componentWillMount(){
fetch("https://api.themoviedb.org/3/search/movie?
api_key=ab85c167dc8f5538f5b6e08f01923243&query=J")
.then((res) =>res.json())
.then((data) => this.setState({
data: data,
}))
}
render() {
return (
<div>
<p>hi i will show you movies</p>
{this.state.data.length !== 0 ?
<Showingmovies mov = {this.state.data}/>:null}
</div>
)
}
}
You need to add return in the map method as
function Showingmovies(props){
return(
<table>
<tbody>
{props.mov.results.map((mv) =>{
return (
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>
)
})}
</tbody>
</table>
)}
Problem is return
Solution
Whenever you use map you must use return if you use `{} like
{props.mov.results &&
props.mov.results.map(mv => {
return <tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>;
})}
and if you are not don't want to return then remove {} like
{props.mov.results && props.mov.results.map(mv =>
<tr>
<td>
<p>image here</p>
</td>
<td>
{mv.original_title}
<p>{mv.overview}</p>
</td>
</tr>)
}
codeSandbox

how to pass search value from one component to another [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
hello veryone im trying to make a simple search bar to search my firebase database based on the search bar queires. im trying to get the input value from 1 component and then in component 2 display the results based on the input value from component 1 using firebase queries. Can someone tell me what im doing wrong ? thanks
1 component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { updateFilter } from '../../actions/filtered';
import { CarsRef, timeRef } from '../admin/reference';
import Searchresults from './searchresult';
class TopSearchBar extends Component {
state = {
make: '',
body: '',
priceRange: ''
}
handleSubmit = (e) => {
e.preventDefault();
this.props.updateFilter(this.state);
this.props.history.push('/cars2')
}
render(){
let content = null;
if (content !==null ) {
<Searchresults make={this.state.make} />
}
else {
<p> h </p>
}
let searchMake = ["Audi","Honda","Lamborghini","Maserati", "Subaru","Toyota"];
let cars = {
Audi: [ {body: "Sedan"}, {body: "Coupe"}, {body: "SUV"} ],
Honda: [ {body: "Sedan"}, {body: "Van"}, {body: "SUV"} ],
Lamborghini: [ {body: "Coupe"} ],
Maserati: [ {body: "Coupe"}, {body: "SUV"} ],
Mercedes: [ {body: "Convertible"}, {body: "Coupe"} ],
Subaru: [ {body: "Hatchback"} ],
Toyota: [ {body: "SUV"}, {body: "Sedan"}, {body: "Truck"} ]
}
let makes = searchMake.map((make, i) => {
return (
<option key={i} value={make}>
{make}
</option>
)
})
if(this.state.make) {
var body = cars[this.state.make].map((make, i) => {
return (
<option key={i} value={make.body}>
{make.body}
</option>)
})
}
return(
<div className="top-search-bar">
<div className="container text-align">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-3">
<select className="form-control"
value={this.state.make}
onChange={(e)=>this.setState({make: e.target.value})}
>
<option value="" disabled selected>Make</option>
{makes}
</select>
</div>
<div className="col-3">
<select className="form-control"
value={this.state.body}
onChange={(e)=>this.setState({body: e.target.value})}
>
<option value="" disabled selected>Body Type</option>
{ makes ? body : null }
</select>
</div>
<div className="col-3">
<select className="form-control"
value={this.state.priceRange}
onChange={(e)=>this.setState({priceRange: e.target.value})}
>
<option value="" disabled selected>Price</option>
<option value="lowHigh">Low To High</option>
<option value="highLow">High To Low</option>
</select>
</div>
<div className="col-3">
<button type="submit" className="btn btn-success btn-block" onClick={this.handleSubmit}>Search</button>
</div>
</div>
{content}
</form>
</div>
</div>
);
}
}
2nd component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Car from './car';
import { CarsRef, timeRef } from '../admin/reference';
import { CardImg, Button, Row, Col, Table } from 'reactstrap';
import { getsinglecar } from '../../actions/cars';
import Icon from 'react-icons-kit';
import { bin } from 'react-icons-kit/icomoon';
import { getCarsThunk } from '../../actions/cars';
import { Link } from 'react-router-dom';
class Searchresults extends Component {
// ...
state = {
Cars: [],
Carsloading: true
};
handleDelete = (id) => {
this.props.deleteCar(id);
}
componentWillMount() {
alert(`${this.props.make}`); // returns undefined
CarsRef.orderByChild(`${this.props.make}`).startAt(`${this.props.make}`).endAt(`${this.props.make}`).on('value', (snap) => {
let Cars = []
snap.forEach((child) => {
Cars.push({ ...child.val(), key: child.key });
});
console.log(Cars);
this.setState({ Cars: Cars, CarsLoading: false });
});
}
render() {
const { Cars, CarsLoading } = this.state;
const orderedcars = Cars;
let carList;
if (CarsLoading) {
carList = <div className="TaskList-empty">Loading...</div>;
} else {
carList = (
<ul className="TaskList">
{Cars.map(car => (
<div>
<Row>
<Col md="12">
<div className="card border-secondary mb-3">
<div className="card-header text-success">
<h4>
<Link to={`/cars/${car.id}`}>
</Link>
</h4>
</div>
<div className="card-body">
<Row>
<Col md="5">
<CardImg
className="carlist-margin"
top
width="100%"
src={car.link}
alt={car.make}
/>
</Col>
<Col md="4">
<Table className="striped">
<tbody>
<tr>
<td>Engine:</td>
<td>{car.engine}</td>
</tr>
<tr>
<td>Drive Type:</td>
<td>{car.drive_type}</td>
</tr>
<tr>
<td>Body:</td>
<td>{car.body_type}</td>
</tr>
<tr>
<td>Exterior Color:</td>
<td>{car.ext_color}</td>
</tr>
<tr>
<td>Interior Color:</td>
<td>{car.int_color}</td>
</tr>
<tr>
<td>Transmission:</td>
<td>{car.transmission}</td>
</tr>
<tr>
<td>VIN:</td>
<td>{car.vin}</td>
</tr>
</tbody>
</Table>
</Col>
<Col md="3">
<Table className="striped">
<tbody>
<tr>
<td className="text-primary text-right">
<strong>MSRP:</strong>
</td>
<td className="text-primary text-right">
<strong>${car.price}</strong>
</td>
</tr>
<tr>
<td className="text-danger text-right">
Dealer Discount:
</td>
<td className="text-danger text-right">
{car.sale}%
</td>
</tr>
<tr>
<td className="text-primary text-right">
<strong>Total:</strong>
</td>
<td className="text-primary text-right">
<strong>
${car.price - (car.price * car.sale) / 100}
</strong>
</td>
</tr>
<tr>
<td className="text-primary text-right">
Est. Lease:
</td>
<td className="text-primary text-right">
$230/m*
</td>
</tr>
<tr>
<td className="text-primary text-right">
Est. Finance:
</td>
<td className="text-primary text-right">
$330/m*
</td>
</tr>
<tr>
<td className="text-right" />
<td className="text-right">
<Link to={`/cars/${car.id}`}>
<Button className="btn btn-success">
More
</Button>
</Link>
</td>
</tr>
</tbody>
</Table>
</Col>
</Row>
</div>
</div>
</Col>
</Row>
</div>
))}
</ul>
);
}
return carList;
}
}
export default Searchresults
;
You have to connect your 2nd component to the Redux Store and create a MapStateToProps to have access to the properties you need.
First create the MapStateToProps
const mapStateToProps = state => {
return {
make: state.make// place here the name of the property in your reducer
}
}
Then connect it to the Redux Store.
export default connect(mapStateToProps)(Searchresults)
From now on you will have access the prop "make" (or whatever name you give in on your MapStateToProps).
Take a look at this example from Redux Documentation
If you show me your actions, I may be more specific in my response.

How do I iterate through table rows and cells in Reactjs

I am pretty new to Reactjs and i have a component that loads json from my db that looks like this
Array[2]
0: Object
_id: "56cf587fe46adb3b8960afe2"
price: 2000
title: "ps3"
url: "www.google.com"
__proto__: Object
1: Object
_id: "56db2bd434df9046e0643d22"
price: 499
title: "HENRIKSDAL"
url: "http://www.ikea.com/se/sv/catalog/products/S59847817/"
__proto__: Object
length: 2
__proto__: Array[0]
what i want to do is to load the data on to the table that looks like this
//start of loop
<tr>
<td className="col-sm-8 col-md-6">
<div className="media">
<div className="media-body">
<h4 className="media-heading">Product name</h4>
</div>
</div></td>
<td className="col-sm-1 col-md-1" styles="text-align: center">
<input type="number" name="quantity" min="1" max="10" className="form-control"/>
</td>
<td className="col-sm-1 col-md-1 text-center"><strong>500:-</strong></td>
<td className="col-sm-1 col-md-1 text-center"><strong>$14.61</strong></td>
<td className="actions" data-th="">
<button className="btn btn-info btn-sm"><i className="fa fa-refresh"></i></button>
<button className="btn btn-danger btn-sm"><i className="fa fa-trash-o"></i></button>
</td>
</tr>
<tr>
<td>   </td>
<td>   </td>
<td>   </td>
<td><h3>Total</h3></td>
<td className="text-right"><h3><strong>$31.53</strong></h3></td>
</tr>
//end of loop
But i don't know how to iterate through the array so it will create a tablerow for each object in the array. Any suggestions?
You can map the array and pass the data through to the html
{this.state.data.map(( listValue, index ) => {
return (
<tr key={index}>
<td>{listValue.id}</td>
<td>{listValue.title}</td>
<td>{listValue.price}</td>
</tr>
);
})}
You can use map method (available in prototype of Array).
Iterating can be as simple as this...
const rows = [
{
_id: "56cf587fe46adb3b8960afe2",
price: 2000,
title: "ps3",
url: "www.google.com",
}, {
_id: "56db2bd434df9046e0643d22",
price: 499,
title: "HENRIKSDAL",
url: "http://www.ikea.com/se/sv/catalog/products/S59847817/",
}
];
var Hello = React.createClass({
renderRow(props) {
return (
<tr>
<td>{ props._id }</td>
<td>{ props.price }</td>
<td>{ props.title }</td>
<td>{ props.url }</td>
</tr>
);
},
render: function() {
return (
<table>
{ this.props.rows.map(this.renderRow) }
</table>
);
}
});
ReactDOM.render(
<Hello rows={rows} />,
document.getElementById('container')
);
Working Fiddle
https://jsfiddle.net/zwdjmozn/1/
This is an extension of #Andreyco answer where a separate template is defined however in my case I am referencing the JSX template within the map call (<Row />
const Table = (props) => (
<div>
<p>Your Table</p>
<table>
<tbody>
{props.rows.map((x,i) => <Row key={i} data={x} />) }
</tbody>
</table>
</div>
)
const Row = (props:any) => (
<tr>
<td>{props.data[0]}</td>
<td>{props.data[1]}</td>
<td>{props.data[2]}</td>
</tr>
)
See JSFiddle

Resources