Refreshing sorted table in React and state issue - reactjs

I tried to sort an array in React, but I don't know how to refresh it. If I set data in a state like this: (data: this.props.data) pagination isn't working. Why is that?
render() {
let data = this.props.data;
return (
<div className='container'>
<table>
<thead>
<tr>
<th>iD</th>
<th>First name</th>
<th>Last name</th>
<th>Birth date</th>
<th onClick={() => {data.sort()}}>Company</th>
<th>Note</th>
</tr>
</thead>
<tbody>
{data.map((user) => {
return (
<tr key={user.id}>
<td className="number">{user.id}</td>
<td className="firstname">{user.firstName}</td>
<td className="lastname">{user.lastName}</td>
<td className="date">{user.dateOfBirth}</td>
<td className="company">{user.company}</td>
<td className="note">{user.note}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}

Check the code below
state = {
//use constructor or es7
data:this.props.data
}
_handleSort=()=>{
/**
* Define you short logic here.
*/
let sortedDate = this.state.data.dateOfBirth.sort()
this.setstate({
data:sortedDate
})
}
render() {
let {data} = this.state;
return (
<div className='container'>
<table>
<thead>
<tr>
<th>iD</th>
<th>First name</th>
<th>Last name</th>
<th>Birth date</th>
<th onClick={() => {this._handleSort}>Company</th>
<th>Note</th>
</tr>
</thead>
<tbody>
{data.map((user) => {
return (
<tr key={user.id}>
<td className="number">{user.id}</td>
<td className="firstname">{user.firstName}</td>
<td className="lastname">{user.lastName}</td>
<td className="date">{user.dateOfBirth}</td>
<td className="company">{user.company}</td>
<td className="note">{user.note}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}

The best way of fix this problem is sort array in parent component.
Instesd of data.sort() you will call this.props.sort() property and your table component will be updated with sorted data.
render() {
let data = this.props.data;
return (
<div className='container'>
<table>
<thead>
<tr>
<th>iD</th>
<th>First name</th>
<th>Last name</th>
<th>Birth date</th>
<th onClick={this.props.sort}>Company</th>
<th>Note</th>
</tr>
</thead>
<tbody>
{data.map((user) => {
return (
<tr key={user.id}>
<td className="number">{user.id}</td>
<td className="firstname">{user.firstName}</td>
<td className="lastname">{user.lastName}</td>
<td className="date">{user.dateOfBirth}</td>
<td className="company">{user.company}</td>
<td className="note">{user.note}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
Parent :
class Parent extends Component {
constructor() {
super(props)
this.state = {
data: ....
}
}
sort = () => {
this.setState({
data: sortDataHere !!!!
})
}
render() {
return (
<Table
data={this.state.data}
sort={this.sort}
/>
)
}
}

Your component isn't properly controlled, since you're just using a reference obtained from props, which doesn't trigger render(). Components should instead be controlled on either state or props to trigger render() changes.
Here's an example to demonstrate that this pattern doesn't work:
class Example extends React.Component {
render () {
let text = this.props.data
return (
<div>
{text}
<button onClick={()=> (text='bye')}>
Click Me
</button>
</div>
)
}
}
ReactDOM.render(<Example data='hello' />, document.getElementById('container'))
<div id='container'></div>
<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>
What you are trying to do is control the component. You have two main options:
1) A stateful approach:
constructor (props) {
super(props)
this.state = { data: props.data }
}
render () {
return (
<th onClick={() => {sortTable.bind(this)}}>Company</th>
)
}
sortTable () {
let { data } = this.state
// sort algorithm,
this.setState({ data })
}
2) A Stateless Pattern using props and something like Redux
This is usually the preferred methodology as it keeps components purely as presentation without logic, which tend to be more reusable.
class Example extends React.Component {
render () {
const { data } = this.props
return (
<th onClick={() => this.props.sortData(data)}>Company</th>
)
}
}
const mapStateToProps = (state) => {
return {
data: state.data
}
}
const mapDispatchToProps = (dispatch) => {
return {
sortData: data => {
dispatch({ type: SORT, payload: data })
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Example)
Then in a state reducer:
export default function (state, action) {
switch (action.type) {
case SORT:
// transform the data
}
}
I tried to keep this above example minimal, and as such it will not work as is. It purely demonstrates how the connected component might look. To fully use redux you need to setup your application with a <Provider> and a store.

Related

How to pass an array as a prop and render in child component in typescript react hooks

I have difficulty passing an array as a prop to a component from the parent and rendering the same in react typescript.
Here is the parent component where the array is been passed.
import ReportComponent from '../Components/Reports/ReportComponent';
import { ReportData } from "../Types/ReportData.types";
const Report = () => {
const [Report, setReport] = useState<ReportData[]>([]);
ReportService.GetReport()
.then((response) => {
console.log(response.data.data);
setReport(response.data.data);
toast.success(response.data.message);
}).catch((e) => {
console.log(e);
});
return <ReportComponent report {...Report}/>;
But I discovered that the array is not getting to the child and I am getting is
TypeError: Report.map is not a function
import { ReportData } from "../../Types/Report.types";
const ReportComponent = (props:ReportData) => {
console.log("props",props)
const [Report, setReport] = useState<ReportData[]>([]);
setReport(props)
return <div className="row">
<div className="table-responsive">
{ Report.map((report)=>(
<table className="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">UID</th>
<th scope="col">Value</th>
<th scope="col">Length</th>
</tr>
</thead>
<tbody className="table table-striped table-sm">
<tr>
<td>{report.name}</td>
<td>{report.UID}</td>
<td>{report.Value}</td>
<td>{report.Length}</td>
</tr>
</tbody>
</table>
))}
</div>
</div>
}
TL;DR:
const Report = () => {
const [report, setReport] = useState<ReportData[]>([]);
useEffect(() => {
ReportService.GetReport()
.then((response) => {
console.log(response.data.data);
setReport(response.data.data);
toast.success(response.data.message);
}).catch((e) => {
console.log(e);
});
}, []);
return <ReportComponent reports={report} />;
);
interface ReportComponentProps {
reports: ReportData[];
}
const ReportComponent = ({
reports,
}: ReportData) => {
return (
<div className="row">
<div className="table-responsive">
{reports.map((report) => (
<table className="table table-striped table-sm">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">UID</th>
<th scope="col">Value</th>
<th scope="col">Length</th>
</tr>
</thead>
<tbody className="table table-striped table-sm">
<tr>
<td>{report.name}</td>
<td>{report.UID}</td>
<td>{report.Value}</td>
<td>{report.Length}</td>
</tr>
</tbody>
</table>
))}
</div>
</div>
);
}
You should not do things as fetching data (ReportService.GetReport()...) inside the render of a component. If you do, every time a component re-renders, that code is executed again, meaning a new fetch will happen.
Passing props is done like <YourComponent propA={propValue} />
Your props do not have the type of ReportData (const ReportComponent = (props:ReportData) => {. props is an object with attributes with the names of the actual props.
You should not do setState inside the render. Just like the fetch, every time the component re-renders, that code is executed again. Because a setState causes a re-render, that means that the "render code" is executed again, so another setState is executed, that causes another re-render, and so on.
If you recive props by properties, you do not need (and should not) do a setState(props). It is not only redundant, but also causes performance losses.
There are a few other issues with your code. I encourage you to go through the baiscs of react again.

Getting Json using Axios and iterating over data in ReactStrap Table

I am trying to get data from jsonplaceholder via axios and iterate over that data and put that data into a reactstrap table. I am getting the error: Expected an assignment or function call and instead saw an expression. I'm not entirely sure what I am doing wrong here. Any help is greatly appreciated.
Here is my code:
render() {
const data = axios.get("https://jsonplaceholder.typicode.com/todos")
.then(response =>
this.data = response.data,
this.data.forEach((item) => {
<tr>
<td>{item.userId}</td>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.completed}</td>
</tr>
})
)
return (
<div className="App">
<header className="App-header">
<Table>
<thead>
<tr>
<th>
User ID
</th>
<th>
ID
</th>
<th>
Title
</th>
<th>
Completed
</th>
</tr>
</thead>
<tbody>
{
data
}
</tbody>
</Table>
</header>
</div>
);
}
}
The error is where I try to create the table row <tr> tags in my data variable.
You should use lifecycle methods to load your data from API and store them in a state and render them when the state is updated.
Try this
import React, { Component } from 'react';
import axios from 'axios';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
todos: []
}
}
componentDidMount() {
axios.get("https://jsonplaceholder.typicode.com/todos")
.then(response => {
this.setState({
todos: this.data
});
})
}
render() {
const { todos = [] } = this.state;
return (
<div className="App">
<header className="App-header">
<Table>
<thead>
<tr>
<th>User ID</th>
<th>ID</th>
<th>Title</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
{todos.length ?
todos.map(todo => (
<tr>
<td>{todo.userId}</td>
<td>{todo.id}</td>
<td>{todo.title}</td>
<td>{todo.completed}</td>
</tr>
))
:
(<tr>
<td>-</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>)
}
</tbody>
</Table>
</header>
</div>
);
}
}
export default Example;
The mistake is here:
axios.get('https://jsonplaceholder.typicode.com/todos').then(response => {
console.log(response);
this.setState({
todos: response.data, // you have it as this.data not response
});
});

Map data to table in ReactJS

I have successfully retrieved data from my API and set that data to setOfAllBooks state. I want to map the data in setOfAllBooks to a within the component. The page loads with the header alright but my data isn't there. I think there should be something wrong with mmy map() function.
import React, { Component } from 'react';
import './ViewAll.css';
import axios from 'axios'
const rootURL = 'http://localhost:5000';
const TableRow = ({ row }) => (
<tr class="table-light">
<th scope="row" key={row.title}>{row.title}</th>
<td key={row.author}>{row.author}</td>
<td key={row.isbn}>{row.isbn}</td>
<td key={row.isbn}>24</td>
</tr>
)
const Table = ({data}) => (
<table class="table table-hover">
<thead>
<tr class="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row => {
<TableRow row={row} />
})}
</tbody>
</table>
)
class ViewAll extends Component {
constructor(props){
super(props);
this.state = {
setOfAllBooks: []
}
}
componentDidMount(){
axios.get(`${rootURL}/api/book/viewAll`)
.then(res => {
this.setState({ setOfAllBooks: res.data });
console.log(this.state.setOfAllBooks)
})
}
render(){
return(
<div>
<Table data={this.state.setOfAllBooks} />
</div>
)
}
}
export default ViewAll;
You missed return inside the .map call.
{data.map(row => {
// Missing return here. Add return, otherwise
// callback function of the map returns undefined
// which is the default return value of each functions
// in JS
<TableRow row={row} />
// return <TableRow row={row} /> will fix it.
})}
Or write the implicit return version of the arrow function.
{data.map(row => <TableRow row={row} />)}

React 'this' Context within Method

I am trying to call a Method on a Button click like this (the button is returned within a method itself):
<button onClick={removeClick}>Remove</button>
this is the method i am trying to call
removeClick = event => {
console.log('clicked');
};
im always getting errors like '_this2 is undefined'
i have already tried binding the method in the constructor.
an arrow function within the onClick didn't work as well
removeClick = event => {
console.log('clicked');
};
renderWeather(cityData) {
const name = cityData.city.name;
const temps = cityData.list.map(weather => weather.main.temp - 273.15);
const pressures = cityData.list.map(weather => weather.main.pressure);
const humidities = cityData.list.map(weather => weather.main.humidity);
const { lon, lat } = cityData.city.coord;
return (
<tr key={lon + lat}>
<td>
<GoogleMap lon={lon} lat={lat} />
<span className="city-name">{name}</span>
</td>
<td>
<WeatherChart data={temps} color="red" unit="°C" />
</td>
<td>
<WeatherChart data={pressures} color="orange" unit="hPA" />
</td>
<td>
<WeatherChart data={humidities} color="blue" unit="%" />
</td>
<td>
<button onClick={this.removeClick}>Remove</button>
</td>
</tr>
);
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>City</th>
<th>Temperature (°C) </th>
<th>Pressure (hPA)</th>
<th>Humidity (%)</th>
<th />
</tr>
</thead>
<tbody>{this.props.weather.map(this.renderWeather)}</tbody>
</table>
);
}
}
const mapStateToProps = ({ weather }) => ({
weather
});
const mapDispatchToProps = {
removeCity
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(WeatherList);
this is how the my component looks like
I hope anyone can help me
try
renderWeather = cityData => {
...
}
and because you are using your clickhandler for a set of data you may want to pass some kind of identifier to it
removeClick = id => event => {
console.log(`Removing data for ${id}`)
}
and call it by
<button onClick={() => {this.removeClick(id)}}>Remove</button>
Try to pass this as a second param to map
<tbody>{this.props.weather.map(this.renderWeather, this)}</tbody>
class Item extends React.Component {
removeClick = event => {
console.log('clicked');
}
renderWeather(cityData) {
return <button onClick={this.removeClick}>Remove</button>
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>City</th>
<th>Temperature (°C) </th>
<th>Pressure (hPA)</th>
<th>Humidity (%)</th>
<th />
</tr>
</thead>
<tbody>{this.props.weather.map(this.renderWeather, this)}</tbody>
</table>
)
}
}
const weather = [
{
city: {name: '123'}
}
];
ReactDOM.render(
<Item weather={weather} />,
document.getElementById('root')
)
<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='root'></div>

Running function without events in render

I'm new with ReactJS and want to ask how functions works here. I have a class, function and render like this :
class MainTable extends React.Component {
constructor(props) {
super(props);
this.state = {
results: []
};
this.getREST = this.getREST.bind(this);
}
getREST() {
console.log(this.props.str)
axios.get(this.props.str)
.then(res => {
const results = res.data.results.map(obj => obj);
this.setState({results});
});
console.log(this.state.results);
}
render() {
return (
<Table hover striped bordered hover responsive size="sm">
<thead>
<tr>
<th>Name</th>
<th>Name</th>
<th>Name</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{this.state.results.map(result =>
<tr key={result.Name}>
<td>{result.Name}</td>
<td>{result.Name}</td>
<td>{result.Name}</td>
<td>{result.Name}</td>
</tr>
)}
</tbody>
</Table>
);
}
}
I can run getRest() function with button event using something like this
<button onClick={this.handleClick.bind(this)} value="Click me" />
But how can i run getRest() function without any events, just in render()?
You should fetch your data in componentDidMount lifecycle method, not in render.
componentDidMount(){
axios.get(this.props.str)
.then(res => {
const results = res.data.results.map(obj => obj);
this.setState({results});
});
}

Resources