Pass a parameter to a prop that is actually a component? - javascript

My scenario is that I have a table that is generated based on data. In one of the columns I would like to pass in a 'remove' button/component, which is meant to remove the row that it is in.
My issue is that the 'remove' button component needs to be given the row so it can determine which data to remove.
If you look in Table.js, you can see where I render the prop as a component '{col.component}' - But how can I also pass values to the components action?Examples below.
App.js
import React, { Component } from 'react';
import Table from './Table';
import Table from './RemoveButton';
class App extends Component {
//Data is the array of objects to be placed into the table
let data = [
{
name: 'Sabrina',
age: '6',
sex: 'Female',
breed: 'Staffordshire'
},
{
name: 'Max',
age: '2',
sex: 'Male',
breed: 'Boxer'
}
]
removeRow = name => {
//Remove object from data that contains name
}
render() {
//Columns defines table headings and properties to be placed into the body
let columns = [
{
heading: 'Name',
property: 'name'
},
{
heading: 'Age',
property: 'age'
},
{
heading: 'Sex',
property: 'sex'
},
{
heading: 'Breed',
property: 'breed'
},
{
heading: '',
component: <RemoveButton action=removeRow()/>
}
]
return (
<>
<Table
columns={columns}
data={data}
propertyAsKey='name' //The data property to be used as a unique key
/>
</>
);
}
}
export default App;
RemoveButton.js
import React from 'react';
const RemoveButton = action => {
return(
<button onClick={action}>Remove Row</button>
)
}
export default RemoveButton;
Table.js
const Table = ({ columns, data, propertyAsKey }) =>
<table className='table'>
<thead>
<tr>{columns.map(col => <th key={`header-${col.heading}`}>{col.heading}</th>)}</tr>
</thead>
<tbody>
{data.map(item =>
<tr key={`${item[propertyAsKey]}-row`}>
{columns.map(col => {
if(col.component){
return(<td> key={`remove-${col.property}`}>{col.component}</td>)
} else {
return(<td key={`${item[propertyAsKey]}-${col.property}`}>{item[col.property]}</td>)
}
})}
</tr>
)}
</tbody>
</table>

Instead of passing down a component in the column, you could pass down the removeRow function to the Table component as a regular prop, and have another value on the remove column to indicate when you should render the remove button for that column, and pass the item name when you invoke it.
class App extends React.Component {
state = {
data: [
{
name: "Sabrina",
age: "6",
sex: "Female",
breed: "Staffordshire"
},
{
name: "Max",
age: "2",
sex: "Male",
breed: "Boxer"
}
]
};
removeRow = name => {
this.setState(({ data }) => ({
data: data.filter(el => el.name !== name)
}));
};
render() {
let columns = [
{
heading: "Name",
property: "name"
},
{
heading: "Age",
property: "age"
},
{
heading: "Sex",
property: "sex"
},
{
heading: "Breed",
property: "breed"
},
{
heading: "",
removeCol: true
}
];
return (
<Table
columns={columns}
data={this.state.data}
removeRow={this.removeRow}
propertyAsKey="name"
/>
);
}
}
const Table = ({ columns, data, removeRow, propertyAsKey }) => (
<table className="table">
<thead>
<tr>
{columns.map(col => (
<th key={`header-${col.heading}`}>{col.heading}</th>
))}
</tr>
</thead>
<tbody>
{data.map(item => (
<tr key={`${item[propertyAsKey]}-row`}>
{columns.map(col => {
if (col.removeCol) {
return (
<td key={`remove-${col.property}`}>
<button onClick={() => removeRow(item.name)}>
Remove Row
</button>
</td>
);
} else {
return (
<td key={`${item[propertyAsKey]}-${col.property}`}>
{item[col.property]}
</td>
);
}
})}
</tr>
))}
</tbody>
</table>
);
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Related

React-table data flow between outside and inside of hooks.visibleColumns.push invocation

I would like to create an array outside useTable invocation, manipulate the array, and based on the array state make checkbox checked or unchecked. After each click on a button, length of the array is increased by adding one element to the array. When length become greater than 3, the input should be checked.
The problem is that the array state is different inside and outside checked attribute of input. Outside it works as expected: the array length increases. Inside, the array length is equal to initial length of 0.
I have attached code with some logging. I think that the relevant part probably ends with the end of useTable invocation (then is some code which I took from react-table docs with button and mock data, columns added). What changes should I introduce to the code to make it work as I expect?
import React, { useMemo, useState } from 'react';
import { useTable } from 'react-table'
function Table({ columns, data }) {
// neither stateArr nor simpleArr help reach what I would like to
const [stateArr, setStateArr] = useState([]);
let simpleArr = [...stateArr];
const handleOnButtonClick = () => {
console.log("Outside checked: simpleArr, stateArr");
console.log(simpleArr);
console.log(stateArr);
setStateArr([...stateArr, 1]);
// in this case unnecessary, since, as I understand, simpleArr is rendered and (re)assigned above
// simpleArr = [...stateArr];
};
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({
columns,
data,
},
(hooks) => {
hooks.visibleColumns.push((columns) => {
return [
{
id: 'checkedInputs',
Header: () => {
return (<div>
<input type="checkbox"
// working, not most elegant way to combine logging and computing boolean
checked={console.log("Inside checked: simpleArr, stateArr") || console.log(simpleArr)
|| console.log(stateArr) || simpleArr.length > 3 || stateArr.length > 3} />
</div>);
},
Cell: () => {
return (<div>R</div>);
},
},
...columns,
];
});
}
);
return (
<div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
<button type="button" onClick={handleOnButtonClick}>Click Me!</button>
</div>
)
}
function App() {
const columns = useMemo(
() => [
{
Header: 'Animal Type',
accessor: 'animalType',
},
{
Header: 'Number of legs',
accessor: 'numberOfLegs',
},
],
[],
);
const data = useMemo(
() => [
{
animalType: 'dog',
numberOfLegs: 4,
},
{
animalType: 'snake',
numberOfLegs: 0,
},
],
[],
);
return (
<Table columns={columns} data={data} />
)
}
export default App;
Stale Data
The hooks.visibleColumns.push function is called one time when the table is created. It creates a Header render component that takes some props and returns a JSX element. The function which renders the Header based on these props is called every time that the table updates. The function which creates this Header component is called once.
In your example, you create a Header component which prints out some data based on the values of simpleArr and stateArr at the time that it was created, not at the time that it was called.
Table State
If we want our Header component to render with current data then we should get that data from props. The Header is called with quite a lot of props but the one that we will use is state which is the state of the table. We will set the initialState of the table to an object { stateArr: [] }. This gets merged with the standard table state { hiddenColumns: [] }.
The table state is updated through a useReducer hook, so we update it by disptaching an action. We need a custom stateReducer to update the table state based on the contents of the action.
import React, { useMemo } from "react";
import { useTable } from "react-table";
function Table({ columns, data }) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
dispatch,
state
} = useTable(
{
columns,
data,
initialState: {
stateArr: []
},
stateReducer: (newState, action, prevState) => {
console.log(action, newState, newState.stateArr);
switch (action.type) {
case "incrementChecks":
return {
...newState,
stateArr: [...newState.stateArr, action.payload]
};
default:
return newState;
}
}
},
(hooks) => {
hooks.visibleColumns.push((columns) => {
return [
{
id: "checkedInputs",
Header: (props) => {
console.log("header props", props); // so you can see all the data you get
console.log("stateArr", props.state.stateArr);
return (
<input
type="checkbox"
readOnly
checked={props.state.stateArr.length > 3}
/>
);
},
Cell: () => {
return <div>R</div>;
}
},
...columns
];
});
}
);
const handleOnButtonClick = () => {
// payload is the item which we are appending to the array
dispatch({ type: "incrementChecks", payload: 1 });
};
console.log("stateArr", state.stateArr);
return (
<div>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<button type="button" onClick={handleOnButtonClick}>
Append
</button>
</div>
);
}
function App() {
const columns = useMemo(
() => [
{
Header: "Animal Type",
accessor: "animalType"
},
{
Header: "Number of legs",
accessor: "numberOfLegs"
}
],
[]
);
const data = useMemo(
() => [
{
animalType: "dog",
numberOfLegs: 4
},
{
animalType: "snake",
numberOfLegs: 0
}
],
[]
);
return <Table columns={columns} data={data} />;
}
export default App;
CodeSandbox Link

Trying to sort data inside a react table: Reactjs

I am trying to sort the table data based on the field selected from the dropdown. It should alternatively sort between ascending or descending whenever the same field is clicked. I am maintaining a sort object that decides which field is selected and what is the sort order. Then it is sent to lodash orderBy with the field and the order. It does not work
This is what I have tried. Can some one tell me what I am doing wrong. Help is really appreciated.
https://codesandbox.io/s/simple-react-class-component-1n3f9?file=/src/index.js:0-3000
import React from "react";
import ReactDOM from "react-dom";
import "semantic-ui-css/semantic.min.css";
import { Dropdown } from "semantic-ui-react";
import moment from "moment";
import orderby from "lodash.orderby";
const options = [
{ key: 1, text: "Name", value: "name", icon: "sort" },
{ key: 2, text: "Time", value: "time", icon: "sort" },
{ key: 3, text: "Type", value: "type", icon: "sort" }
];
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [],
original: [],
sortObject: { field: "", order: "" }
};
}
componentDidMount() {
let list = [
{
name: "namev1",
time: 1583295463213,
type: 14
},
{
name: "namea2",
time: 1582885423296,
type: 15
},
{
name: "namea3",
time: 1581295463213,
type: 16
}
];
this.setState({ list, original: list });
}
handleSearch = e => {
let searchInput = e.target.value;
let filteredData = this.state.original.filter(value => {
return (
value.name.toLowerCase().includes(searchInput.toLowerCase()) ||
value.type.toString().includes(searchInput.toString())
);
});
this.setState({ list: filteredData });
};
formSortObject = fieldName => {
let { sortObject } = this.state;
if (!sortObject.field || sortObject.field !== fieldName) {
Object.assign(sortObject, {
field: fieldName,
order: "asc"
});
return sortObject;
} else if (sortObject.field === fieldName) {
Object.assign(sortObject, {
...sortObject,
order: sortObject.order === "desc" ? "asc" : "desc"
});
return sortObject;
}
};
handleSort = (e, data) => {
let dropdDownValue = data.value;
let currentField = this.formSortObject(dropdDownValue);
let result = orderby(
this.state.list,
currentField.field,
currentField.order
);
this.setState({ list: result });
};
render() {
return (
<>
Search: <input type="text" onChange={this.handleSearch} />
<Dropdown text="Sort By" options={options} onChange={this.handleSort} />
<h1>List</h1>
<table>
<tbody>
{this.state.list.map((item, index) => (
<tr key={index}>
<td>
<p>{index + 1}</p>
</td>
<td>
<p>{item.name}</p>
</td>
<td>
<p>{moment().diff(item.time, "days")}</p>
</td>
<td>
<p>{item.type}</p>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}
}
Your code isnt's working as you spected because you are calling the handleSort function only when the value of the select change (see the onChange of your <Dropdown />).
What you need is the function executed when an option is clicked.
I searched for the documentation of the library you are using and I came to the conclusion that what you need is this.
<Dropdown text='Sort By'>
<Dropdown.Menu>
{options.map(item=>
<Dropdown.Item text={item.text} value={item.value} icon={item.icon} key={item.key} onClick={this.handleSort}/>
)}
</Dropdown.Menu>
</Dropdown>
I tried it in your codesandbox and it works perfectly!
I hope it helps!

Unable to access the table header data

I'm developing an app using React. However, facing some issue with reflecting the {column.label} at column header field. The code works with any plain text but not for inputs in {}.
Below is the code for reference.
filename: tableHeader.jsx:
import React, { Component } from "react";
class TableHeader extends Component {
raiseSort = (path) => {
const sortColumn = { ...this.props.sortColumn };
if (sortColumn.path === path)
sortColumn.order = sortColumn.order === "asc" ? "desc" : "asc";
else {
sortColumn.path = path;
sortColumn.order = "asc";
}
this.props.onSort(sortColumn);
};
render() {
return (
<thead>
<tr>
{this.props.columns.map((column) => (
<th
key={column.path || column.key}
onClick={() => this.raiseSort(column.path)}
>
{column.label}
</th>
))}
</tr>
</thead>
);
}
}
export default TableHeader;
filename: moviesTable.jsx:
import React, { Component } from "react";
import Like from "./common/like";
import TableHeader from "./common/tableHeader";
class MoviesTable extends Component {
columns = [
{ path: "title", lable: "Title" },
{ path: "genre.name", lable: "Genre" },
{ path: "numberInStuck", lable: "Stock" },
{ path: "dailyRentalRate", lable: "Rate" },
{ key: "Like" },
{ key: "Delete" },
];
render() {
const { movies, onDelete, onLike, onSort, sortColumn } = this.props;
return (
<table className="table">
<TableHeader
columns={this.columns}
sortColumn={sortColumn}
onSort={onSort}
/>
<tbody>
{movies.map((movie) => (
<tr key={movie._id}>
<td> {movie.title} </td>
<td> {movie.genre.name} </td>
<td> {movie.numberInStock} </td>
<td> {movie.dailyRentalRate} </td>
<td>
<Like liked={movie.liked} onClick={() => onLike(movie)} />
</td>
<td>
<button
onClick={() => onDelete(movie)}
className="btn btn-danger btn-sm"
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
}
export default MoviesTable;
fileName: movie.jsx:
import React, { Component } from "react";
import { getMovies } from "../services/fakeMovieService";
import { getGenres } from "../services/fakeGenreService";
import Pagination from "./common/pagination";
import { paginate } from "../utils/paginate";
import ListGorup from "./common/listGroup";
import MoviesTable from "./moviesTable";
import _ from "lodash";
class Movies extends Component {
state = {
movies: [],
genres: [],
pageSize: 4,
currentPage: 1,
sortColumn: { path: "title", order: "asc" },
};
componentDidMount() {
const genres = [{ _id: "", name: "All Genres" }, ...getGenres()];
this.setState({ movies: getMovies(), genres });
}
handleDelete = (movie) => {
const movies = this.state.movies.filter((m) => m._id !== movie._id);
this.setState({ movies });
};
handleLike = (movie) => {
const movies = [...this.state.movies];
const index = movies.indexOf(movie);
movies[index] = { ...movie };
movies[index].liked = !movies[index].liked;
this.setState({ movies });
};
handlePageChange = (page) => {
this.setState({ currentPage: page });
};
handleGenreSelect = (genre) => {
this.setState({ selectedGenre: genre, currentPage: 1 });
};
handleSort = (sortColumn) => {
this.setState({ sortColumn });
};
render() {
const { length: count } = this.state.movies;
const {
pageSize,
currentPage,
movies: allMovies,
selectedGenre,
sortColumn,
} = this.state;
if (count === 0) return <p>No movies in the DB..</p>;
const filtered =
selectedGenre && selectedGenre._id
? allMovies.filter((m) => m.genre._id === selectedGenre._id)
: allMovies;
const sorted = _.orderBy(filtered, [sortColumn.path], [sortColumn.order]);
const movies = paginate(sorted, currentPage, pageSize);
return (
<div>
<div className="row">
<div className="col-3">
<ListGorup
items={this.state.genres}
onItemSelect={this.handleGenreSelect}
selectedItem={this.state.selectedGenre}
/>
</div>
<div className="col">
<p>Showing {filtered.length} movies in the DB. </p>
<MoviesTable
movies={movies}
onLike={this.handleLike}
onDelete={this.handleDelete}
onSort={this.handleSort}
sortColumn={sortColumn}
/>
<Pagination
itemsCount={filtered.length}
onPageChange={this.handlePageChange}
pageSize={pageSize}
currentPage={currentPage}
/>
</div>
</div>
</div>
);
}
}
export default Movies;
The below code from file1(tableHeader.jsx) is responsible to give the column name. Not sure, why it's not coming.
<thead>
<tr>
{this.props.columns.map((column) => (
<th
key={column.path || column.key}
onClick={() => this.raiseSort(column.path)}
>
{column.label}
</th>
))}
</tr>
</thead>
Looking for to hearing from you.
Your columns list only contains objects with the key lable (misspelled). It should be label:
columns = [
{ path: "title", /* not lable ! */ label: "Title" },
{ path: "genre.name", label: "Genre" },
{ path: "numberInStuck", label: "Stock" },
{ path: "dailyRentalRate", label: "Rate" },
{ key: "Like" },
{ key: "Delete" },
];

React Complex Form Editing key value pair

I am building a form to edit a key/value pair object, see picture:
This is the object that I get from the backend that I stored in the container component.
items: [{
name: "Product Name",
sku: "00001",
attributes: {
width: "value",
height: "value",
length: "value",
color: "value"
}
},
{
name: "Product Name 2",
sku: "00002",
attributes: {
description: "Hello World",
weight: "250",
quantity: "500",
hello: "World"
}
}]
I then pass the data into a child component via props. This is the code for the form:
class EditForm extends Component {
render() {
let editForm = this.props.items && this.props.items.map((item, idx) => {
return(
<tr key={item.sku}>
<td className="edit-table">
<input
value={item.name}
name={item.name}
onChange={(e)=> this.props.onChange(e, idx)}
/>
</td>
<td className="edit-table">
<ul className="item-attributes">
{Object.keys(item.attributes).map((key) => {
return (<li key={key}>
<label>{key}</label>
<input
value={item.attributes[key]}
onChange={(e) => this.props.onChange(e, idx) }
/>
</li>)
})}
</ul>
</td>
</tr>
)
})
return(
<table className="editcontainer-table">
<thead>
<tr>
<th>SKU</th>
<th>Attributes</th>
</tr>
</thead>
<tbody>
{editForm}
</tbody>
</table>
);
}
}
Now, this is where I'm stuck, I'm trying to figure out how the onChange function would work for me to edit the objects in the state and send back to the server for updating.
Give the item name input a name="name" attribute
Give the attribute inputs names per the attribute: name={key}
You can now identify what's being edited in onChange - make the appropriate state change based on the index and input name being changed.
e.g. (using import update from 'immutability-helper') onChange might look like this:
onChange = (e, index) => {
let {name, value} = e.target
let updateSpec
if (name === 'name') {
updateSpec = {
[index]: {
name: {$set: value}
}
}
}
else {
updateSpec = {
[index]: {
attributes: {
[name]: {$set: value}
}
}
}
}
this.setState({items: update(this.state.items, updateSpec)})
}
Here's an example app which shows this solution in action:
Live version
Full example source
I would change the onChange function in your Container to be the following:
onChange = (e, sku, field) => {
const updatedItems = this.state.items.map(e => {
if (e.sku !== sku) return e;
if (field === 'name') {
e.name = e.target.value;
} else {
// has to be attributes
e.attributes[field] = e.target.value;
}
return e;
});
this.setState({
items: updatedItems,
})
}
Added sku parameter to identify the item in items array and the field you want to change. Since sku is not going to change, we can use this as the item identifier. This way, you can change the product name and its respective attributes. For full working code, see the code example below.
class Container extends React.Component {
constructor() {
super();
this.state = {
items: [{
name: "Product Name",
sku: "00001",
attributes: {
width: "value",
height: "value",
length: "value",
color: "value"
}
},
{
name: "Product Name 2",
sku: "00002",
attributes: {
description: "Hello World",
weight: "250",
quantity: "500",
hello: "World"
}
}],
}
}
onChange = (e, sku, field) => {
console.log(e.target.value);
const updatedItems = this.state.items.map(item => {
if (item.sku !== sku) return item;
if (field === 'name') {
item.name = e.target.value;
} else {
// has to be attributes
item.attributes[field] = e.target.value;
}
return item;
});
this.setState({
items: updatedItems,
})
}
render() {
// log new state here
console.log(this.state.items);
return <EditForm onChange={this.onChange} items={this.state.items} />
}
}
class EditForm extends React.Component {
render() {
let editForm = this.props.items && this.props.items.map((item, idx) => {
return(
<tr key={item.sku}>
<td className="edit-table">
<input
value={item.name}
name={item.name}
onChange={(e)=> this.props.onChange(e, item.sku, 'name')}
/>
</td>
<td className="edit-table">
<ul className="item-attributes">
{Object.keys(item.attributes).map((key) => {
return (<li key={key}>
<label>{key}</label>
<input
value={item.attributes[key]}
onChange={(e) => this.props.onChange(e, item.sku, key) }
/>
</li>)
})}
</ul>
</td>
</tr>
)
})
return(
<table className="editcontainer-table">
<thead>
<tr>
<th>SKU</th>
<th>Attributes</th>
</tr>
</thead>
<tbody>
{editForm}
</tbody>
</table>
);
}
}
ReactDOM.render(<Container />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

How do you create a complete html like table in reactjs?

I'm learning react and I'm trying to figure out how I would put this information into a table. I know there is
<table></table>
in html so I'm assuming I would use something like that within the javascript. I'm trying to create a table where the month of the birthday contains the name and the date of each person.
Any advice/help would be great!
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
March: [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
render() {
return (
<div>
{Object.keys(this.state.birthdays).reduce((birthdays, month) => {
return birthdays.concat(this.state.birthdays[month].map((bday) => {
return (
<p>{bday.name} - {bday.date}</p>
);
}));
}, [])}
</div>
);
}
ReactDOM.render(<App />,
document.getElementById('container'));
Excuse the formatting of the code.
You need to first map the keys and then iterate over the birthdays object. Also put single quotes on March like 'March'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
'March': [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
renderTableRows = () => {
var rows = [];
Object.keys(this.state.birthdays).forEach((month, key) => { var birthdays = this.state.birthdays[month];
birthdays.forEach((obj) => {
rows.push (
<tr>
<td>{month}</td>
<td >{obj.name}</td>
<td>{obj.date}</td>
</tr>
)
})
})
var tby = null;
tby = <tbody>
{rows.map((obj, key) => {
return (
obj
)
})}
</tbody>
return tby;
}
render() {
return (
<div>
<table className="table table-striped">
<thead>
<tr>
<th>Month</th>
<th>Name</th>
<th>Date</th>
</tr>
</thead>
{this.renderTableRows()}
</table>
</div>
);
}
}
ReactDOM.render(<App />,
document.getElementById('container'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.js"></script>
<div id="container"></div>
You're using jsx which is an extension of javascript by including html tags, this means that you can use them within your javascript code.
You need to open the <table> tag before iterating over your birthdays so you can create the <tr> (rows) inside it.
return <table>
{
birthdays.map((bday) => (
<tr>
<td>{bday.name}</td>
<td>{bday.date}</td>
</tr>
);
}
</table>
I suggest you read more about table elements in html.
One way of presenting your data may be as follows: http://codepen.io/PiotrBerebecki/pen/jrVkdO
Here is the code for the React component class:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
March: [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
render() {
console.log();
return (
<table>
<thead>
<th>
<td>Month</td>
<td>Name</td>
<td>Birthday</td>
</th>
</thead>
<tbody>
{Object.keys(this.state.birthdays).map((month, key) => {
return (
<tr key={key}>
{this.state.birthdays[month].map((person, index) => {
return (
<tr key={String(key) + String(index)}>
<td>{month}</td>
<td>{person.name}</td>
<td>{person.date}</td>
</tr>
);
})}
</tr>
)
})}
</tbody>
</table>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);

Categories

Resources