Unable to access the table header data - javascript

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" },
];

Related

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!

How can I custom my DataTable in React App

I found a tutorial to implement DataTable in React App but I have no idea how can I custom my tr, td, colspan etc...
My DataTable.js is :
import React, { Component } from 'react';
const $ = require('jquery');
$.DataTable = require('datatables.net');
class App extends Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.DataTable({
data: this.props.data,
columns: this.props.columns
})
}
render() {
return (
<div className="table-responsive">
<table className="table" ref={el => this.el = el}>
</table>
</div>
);
}
}
And after in my Test.js :
import React, { Component } from 'react';
import DataTable from './DataTable';
import axios from 'axios';
class Test extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
columns: [
{ title: "Id" },
{ title: "Category" },
{ title: "Title" },
{ title: "Command 1" },
{ title: "Command 2" },
{ title: "Command 3" }
]
}
}
componentDidMount() {
this._getFilteredItems();
}
_getFilteredItems = () => {
axios.post('http://localhost:8080/items/category', { category: "category1" })
.then((res) => {
var test = res.data.map((e) => Object.values(e)); // to transform objet in array
this.setState({ data: test });
})
.catch(error => { console.log(error) });
}
display = () => {
if(this.state.data.length > 0){
return (
<DataTable
data={this.state.data}
columns={this.state.columns}>
</DataTable>
);
}
}
render() {
return (
<div>
{this.display()}
</div>
);
}
}
export default Test;
My data received from my backend is like this :
[
["5e9c231facad1424801f5167", "category1", "title", "command1", "command2", "command3"],
["5e9c2337acad1424801f58ce", "category1", "title", "command1", "command2", "command3"],
["5eaa105b82d1130017d31dbe", "category1", "title", "command1", "command2", "command3"],
]
The thing is I would like to custom my tr, td, colspan etc... I mean, I would like for example put the title with a colspan="5" and my command1, command2 and command3 in the same td.
Do you have any idea how can I do that ? Thanks
I found, I simply initialized my table as I wanted in the render instead of using props like the tutorial mentionned it :
import React, { Component } from 'react';
import axios from 'axios';
const $ = require('jquery');
$.DataTable = require('datatables.net');
class Test extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
this._getFilteredItems();
}
_getFilteredItems = () => {
axios.post('http://localhost:8080/items/category', { category: "category1" })
.then((res) => {
this.setState({data: res.data});
this.$el = $(this.el);
this.$el.DataTable({});
})
.catch(error => { console.log(error) });
}
render() {
return (
<div className="table-responsive">
<table className="table" ref={el => this.el = el}>
<thead>
<tr>
<th>Title</th>
<th>Commands</th>
</tr>
</thead>
<tbody>
{this.state.data.map((elmt, i) => (
<tr key={i}>
<td>
{elmt.title}
</td>
<td>
{elmt.command1} <br/>
{elmt.command2} <br/>
{elmt.command3} <br/>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
}
export default Test;

How to add DatePicker on buttonclick reactjs

I would like to know how to create dynamic row with datepicker as a field on button click.
Need to create row dynamically on button click, row is generated on click.
But Datepicker not working.
But I need to add one field as Datepicker
How to do it in reactjs
I have DynamicTable component which creates row with fields, used in Home
import DynamicTable from "./../../../components/DynamicTable";
import DatePicker from "react-date-picker";
class Home exends React.PureComponent{
constructor(props){
super(props);
this.state={
additionalFields: [
{ date: "", id: 0 }
]
}
handleAddRow = e => {
if (e) e.preventDefault();
const rowLength = this.state.additionalFields.length;
let lastele = this.state.additionalFields[rowLength - 1];
const item = {
date: "",
id: lastele.id + 1
};
this.setState(
{
additionalFields: [...this.state.additionalFields, item]
}
);
};
render() {
return( <DynamicTable
columns={[
{ dataFieldId: "date", label: "Date", addRow: true },
{ dataFieldId: "AD", label: "" }
]}
rows={this.state.additionalFields}
addRow={this.handleAddRow}
/>);
}
}
}
DynamicTable Component
import React, { Component } from "react";
import DatePicker from "react-date-picker";
class DynamicTable extends Component {
constructor(props) {
super(props);
this.state = {
date: new Date();
};
}
handleDatePicker = (value, name, field, row) =>{
//this.props.handleInputChange(value, field, row);
this.setState({ [name] : value });
}
renderRowData = (column, row, col, index, rowId, rowleng) => {
if (column.addRow) {
return (
<td key={`tableview-td-${rowId}-${index}`}>
{column.dataFieldId==="date" ?
<DatePicker
locale = "en-GB"
className="datepicker"
name={"date_"+rowId}
onChange={(e)=>this.handleDatePicker(e, "date_"+rowId , column.dataFieldId, row)}
value={this.state.date}
/>
:"Loading"}
</td>
);
}
tableHeaders = () => (
<thead>
<tr>
{this.props.columns.map((column, index) => {
return <th key={`tableview-th-${index}`}>{column.label}</th>;
})}
</tr>
</thead>
);
tableBody = () => {
var rowleng = this.props.rows ? this.props.rows.length : 0;
return (
<tbody>
{this.props.rows.map(row => {
let index = row.id;
const rowId = `row_${index}`;
return (
<tr key={`tableview-tr-inner-${index}`} id={rowId}>
{this.props.columns.map((column, index) => {
const col = column.dataFieldId.replace(/\s/g, "");
return this.renderRowData(
column, row, col, index, rowId, rowleng
);
})}
</tr>
);
})}
</tbody>
);
};
renderRowData = (column, row, col, index, rowId, rowleng) => {
if (column.addRow) {
return (
<td key={`tableview-td-${rowId}-${index}`}>
<input
type="text"
defaultValue={row[column.dataFieldId]}
placeholder={
column.dataFieldId.charAt(0).toUpperCase() +
column.dataFieldId.slice(1)
}
/>
</td>
);
}
if (col === "AD") {
return (
<td key={`tableview-td-${rowId}-${index}`}>
<img
className="addBtn1"
onClick={this.props.addRow}
src={"/assets/icons/ic_add_blue.png"}
/>
</td>
);
}
return <td key={`tableview-td-${rowId}-${index}`}>{row[col]}</td>;
};
render() {
return (
<React.Fragment>
<div className="dynamicTable">
<table>
{this.tableHeaders()}
{this.tableBody()}
</table>
</div>
</React.Fragment>
);
}
}
export default DynamicTable;
You can pass configured date picker to your child component
state = {
date: new Date(),
}
onChange = date => this.setState({ date })
<DatePicker
onChange={this.onChange}
value={this.state.date}
/>

Search filter does not work across the react-table on setState : React JS

I am using react-table for data grid purposes. All I am trying to implement a search filter that searches data across the table and filters down by using filter method. I have maintained a separate component for Searching and I set the table's data inside search component. The filtering breaks when the characters are being deleted from the search filter. Am I doing something wrong here.
Sandbox: https://codesandbox.io/s/stoic-gould-kw9iq
SearchComponent
import React from "react";
import { Input } from "semantic-ui-react";
export default class GlobalSearchComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
filteredData: [],
searchInput: ""
};
}
handleChange = event => {
this.setState({ searchInput: event.target.value }, () =>
this.globalSearch()
);
};
globalSearch = () => {
let { searchInput } = this.state;
let filteredData = this.props.data.filter(value => {
return (
value.firstName.toLowerCase().includes(searchInput.toLowerCase()) ||
value.status.toLowerCase().includes(searchInput.toLowerCase()) ||
value.visits
.toString()
.toLowerCase()
.includes(searchInput.toLowerCase())
);
});
this.props.handleSetData(
(filteredData.length > 0 && filteredData) || searchInput
? filteredData
: this.props.data
);
};
render() {
return (
<>
<br />
<Input
size="large"
name="searchInput"
value={this.state.searchInput || ""}
onChange={this.handleChange}
label="Search"
/>
<br />
<br />
</>
);
}
}
App Component
import React from "react";
import ReactDOM from "react-dom";
import GlobalSearchComponent from "./GlobalSearchComponent";
import ReactTable from "react-table";
import "react-table/react-table.css";
import "./styles.css";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
columns: [],
searchInput: ""
};
}
componentDidMount() {
this.getData();
this.getColumns();
}
getColumns = () => {
let columns = [
{
Header: "First Name",
accessor: "firstName"
{
Header: "Status",
accessor: "status"
},
{
Header: "Visits",
accessor: "visits"
}
];
this.setState({ columns });
};
getData = () => {
let data = [
{ firstName: "aaaaa", status: "Pending", visits: 155 },
{ firstName: "aabFaa", status: "Pending", visits: 155 },
{ firstName: "adaAAaaa", status: "Approved", visits: 1785 },
{ firstName: "aAaaaa", status: "Approved", visits: 175 },
{ firstName: "adaSaaa", status: "Cancelled", visits: 165 },
{ firstName: "aasaaa", status: "Cancelled", visits: 157 },
{ firstName: "aweaaaaaewea", status: "Approved", visits: 153 },
{ firstName: "adaAAadsdweaa", status: "Approved", visits: 17585 },
{ firstName: "aAaaaa", status: "Approved", visits: 175 }
this.setState({ data });
};
handleSetData = data => {
console.log(data);
this.setState({ data });
};
render() {
let { data, columns } = this.state;
return (
<div>
<GlobalSearchComponent
data={this.state.data}
handleSetData={this.handleSetData}
/>
<ReactTable
data={data}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Don't update the original data, update filtered data in your main app.
SearchComponent
remove filteredData[] from state, and change global search to
globalSearch = () => {
let { searchInput } = this.state;
let filteredData = this.props.data.filter(value => {
return (
value.firstName.toLowerCase().includes(searchInput.toLowerCase()) ||
value.status.toLowerCase().includes(searchInput.toLowerCase()) ||
value.visits
.toString()
.toLowerCase()
.includes(searchInput.toLowerCase())
);
});
this.props.handleSetData(filteredData);
};
App Component
change handleSetData to update filteredData instead of data.
handleSetData = data => {
console.log(data);
this.setState({ filteredData: data });
};
getData = () => {
let data = [...]
this.setState({ data, filteredData: data });
};
// change your render to
render() {
let { filteredData, columns } = this.state;
return (
<div>
<GlobalSearchComponent
data={this.state.data}
handleSetData={this.handleSetData}
/>
<ReactTable
data={filteredData}
columns={columns}
defaultPageSize={10}
className="-striped -highlight"
/>
</div>
);
}
DEMO
In react-table v7+ you can use useGlobalFilter here's how to do it.
import React, { useMemo } from 'react';
import { useGlobalFilter, useTable } from 'react-table';
// sample array
import users from 'mock/users';
boilerplate setup
const data = useMemo(() => users, []);
const columns = useMemo(
() => [
{
Header: 'Name',
accessor: 'name',
},
{
Header: 'Email',
accessor: 'email',
},
],
[]
);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
setGlobalFilter,
} = useTable({
columns,
data
},
useGlobalFilter,
);
render the html
return (
<>
<input
type="search"
placeholder="Search"
onChange={(e) => setGlobalFilter(e.target.value ? e.target.value : undefined)}
/>
<table {...getTableProps({ role: null })}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps({ role: null })}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()} >
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps({ role: null })}>
{rows.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps({ role: null })}>
{row.cells.map((cell) => <td {...cell.getCellProps({ role: null })} >
{cell.render('Cell')}
</td>)}
</tr>
);
})}
</tbody>
</table>
</>
);

React Changing props of component and not render each time

I am learning React and I am trying to use a component to show details of a selected item in a table, my problem is that when I click Next in the table (it is paginated), the state got updated and re-render the component and if I do click many times, it enqueue and the details component is changing between all the items I did click on.
I don't know if there is a good practice for doing this kind of things, i tried to search for something like this but nothing yet.
The component I am using (using public API):
import React from 'react';
class Pokemon extends React.Component {
constructor() {
super();
this.state = {
name: "",
abilities: [],
stats: [],
weight: 0,
height: 0,
base_experience: 0,
};
}
fetch_pokemon = () => {
fetch(this.props.pokemon_url)
.then(res => res.json())
.then(res => {
this.setState({
name: res.name,
abilities: res.abilities,
stats: res.stats,
weight: res.weight,
height: res.height,
base_experience: res.base_experience,
});
});
}
render() {
if(this.props.pokemon_url !== ''){
this.fetch_pokemon();
return (
<div>
<h1>{this.state.name}</h1>
Weight: {this.state.weight}<br />
Height: {this.state.height}<br />
Abilities:
<ul>
{this.state.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
);
}
else{
return (<h3>Choose one pokémon from the list...</h3>);
}
}
}
export default Pokemon;
My main component:
import React, { Component } from 'react';
//import logo from './logo.svg';
import './App.css';
import Pokemon from './Pokemon';
class App extends Component {
constructor() {
const i_pokemons = 25;
super();
this.state = {
pokemons: [],
pokemons_list: [],
pokemon_show_list: [],
p_init: 0,
p_final: i_pokemons,
pagination: i_pokemons,
pages: 0,
status: '',
enable_buttons: false,
current_page: 0,
current_pokemon_url: '',
URL: `https://pokeapi.co/api/v2/pokemon/?limit=99999`
};
}
componentDidMount(){
this.fetch_pokemons();
}
prev(){
this.setState({
current_page: this.state.current_page - 1,
p_init: this.state.p_init - this.state.pagination,
p_final: this.state.p_final - this.state.pagination
}, () => {
this.fetch_new_page();
});
}
next(){
this.setState({
current_page: this.state.current_page + 1,
p_init: this.state.p_init + this.state.pagination,
p_final: this.state.p_final + this.state.pagination
}, () => {
this.fetch_new_page();
});
}
fetch_new_page = () => {
const current_id = (this.state.current_page - 1) * this.state.pagination;
this.setState({
pokemon_show_list: this.state.pokemons_list.slice(current_id, current_id + 25)
});
this.fetch_pokemons();
}
fetch_pokemons = callback => {
this.setState({
status: 'Waiting for the server and retrieving data, please wait...',
enable_buttons: false
});
return new Promise((resolve, reject) => {
fetch(this.state.URL)
.then(res => res.json())
.then(res => {
if(!res.detail){
this.setState({
pokemons: res,
pokemons_list: res.results,
enable_buttons: true,
status: 'Done',
pokemon_show_list: res.results.slice(this.state.p_init, this.state.p_final)
});
if(this.state.pages === 0){
this.setState({
pages: Math.round(this.state.pokemons_list.length / this.state.pagination),
current_page: 1
});
}
resolve(true);
}else{
reject("Error");
this.setState({status: `Error`});
}
})
.catch(error => {
this.setState({status: `Error: ${error}`});
reject(error);
});
});
}
showPokemon({url}){
this.setState({
current_pokemon_url: url
});
}
render() {
console.log("Render");
return(
<div className="general">
<div className="pokemons-info">
{this.state.status !== '' && this.state.status}
<br />
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{this.state.pokemon_show_list.map((pokemon, index) => {
return (
<tr className="l" key={index}>
<td>{pokemon.name}</td>
<td><a className="btn btn-secondary" onClick={this.showPokemon.bind(this, pokemon)} href={`#${pokemon.name}`}>More info.</a></td>
</tr>
);
})}
</tbody>
</table>
<button className="btn btn-primary" disabled={this.state.current_page <= 1} onClick={this.prev.bind(this)}>Prev</button>
Page: {this.state.current_page} of {this.state.pages}
<button className="btn btn-primary" disabled={this.state.current_page === this.state.pages} onClick={this.next.bind(this)}>Next</button>
</div>
<Pokemon pokemon_url={this.state.current_pokemon_url}/>
</div>
);
}
}
export default App;
Feel free for giving any advice
I refactored and clean your code a little bit, but I guess the code below aims what you are looking for. (Read more about functional component 'logicless components').
const API = 'https://pokeapi.co/api/v2/pokemon/';
const PAGE_SIZE = 25;
function Status(props) {
return (
<div>{props.value}</div>
)
}
function Pagination(props) {
return (
<div>
<button
onClick={props.onPrevious}
disabled={props.disabled}>
Prev
</button>
<button
onClick={props.onNext}
disabled={props.disabled}>
Next
</button>
</div>
)
}
function Pokemon(props) {
return (
<div>
<h1>{props.pokemon.name}</h1>
Weight: {props.pokemon.weight}<br />
Height: {props.pokemon.height}<br />
Abilities:
<ul>
{props.pokemon.abilities.map(({ability}, index) => {
return (<li key={index}>{ability.name}</li>);
})}
</ul>
</div>
)
}
function PokemonTable (props) {
return (
<table className="pokemon-list">
<thead>
<tr>
<th>Name</th>
<th>More info.</th>
</tr>
</thead>
<tbody>
{props.children}
</tbody>
</table>
);
}
function PokemonRow (props) {
return (
<tr>
<td>{props.pokemon.name}</td>
<td>
<a href="#" onClick={() => props.onInfo(props.pokemon)}>
More info.
</a>
</td>
</tr>
);
}
class App extends React.Component {
state = {
pokemons: [],
detailedPokemons : {},
loading: false,
status : null,
previous : null,
next : null
}
componentDidMount () {
this.getPokemons(`${API}?limit=${PAGE_SIZE}`)
}
request(url) {
return fetch(url)
.then(blob => blob.json());
}
getPokemons (url) {
this.setState(state => ({
...state,
loading : true,
status : 'Fetching pokemons...'
}));
this.request(url)
.then(response => {
console.log(response)
this.setState(state => ({
...state,
previous : response.previous,
next : response.next,
pokemons : response.results,
loading : false,
status : null
}))
})
.catch(err => {
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
getPokemonDetail (pokemon) {
const { detailedPokemons } = this.state;
const cachePokemon = detailedPokemons[pokemon.name];
if (cachePokemon !== undefined) { return; }
this.setState(state => ({
...state,
loading : true,
status : `Fetching ${pokemon.name} info`
}));
this.request(pokemon.url)
.then(response => {
this.setState(state => ({
...state,
loading: false,
status : null,
detailedPokemons : {
...state.detailedPokemons,
[response.name]: {
name: response.name,
abilities: response.abilities,
stats: response.stats,
weight: response.weight,
height: response.height,
base_experience: response.base_experience
}
}
}))
})
.catch(err => {
console.log(err)
this.setState(state => ({
...state,
loading : false,
status : 'Unable to retrieved pockemons'
}));
});
}
renderPokemons () {
const { pokemons } = this.state;
return pokemons.map(pokemon => (
<PokemonRow
pokemon={pokemon}
onInfo={this.handleView}
/>
));
}
renderDetailPokemons () {
const { detailedPokemons } = this.state;
return (
<ul>
{Object.keys(detailedPokemons).map(pokemonName => (
<li key={pokemonName}>
<Pokemon pokemon={detailedPokemons[pokemonName]}/>
</li>
))}
</ul>
)
}
handleView = (pokemon) => {
this.getPokemonDetail(pokemon);
}
handlePrevious = () => {
const { previous } = this.state;
this.getPokemons(previous);
}
handleNext = () => {
const { next } = this.state;
this.getPokemons(next);
}
render () {
const { loading, detailedPokemons, status, next, previous } = this.state;
return (
<div className='general'>
<div className="pokemons-info">
{ status && <Status value={status} /> }
<PokemonTable>
{this.renderPokemons()}
</PokemonTable>
<Pagination
disabled={loading}
onPrevious={this.handlePrevious}
onNext={this.handleNext}
/>
{
Object.keys(detailedPokemons).length > 0 &&
this.renderDetailPokemons()
}
</div>
</div>
)
}
}
ReactDOM.render(
<App />,
document.querySelector('#app')
);

Categories

Resources