Running function without events in render - reactjs

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});
});
}

Related

How to get React to wait for state to be set to render

userAccess is a function from another component which returns an array that I am trying to turn into a state variable and display it in render, however unless I implement an onclick=findSubs, the render does not show the correct state variable values. I thought putting it in componentDidMount would do something but it did not
import {userAccess} from '../../firebase';
class MySubscriptions extends Component{
constructor(props) {
super(props);
this.state = {subs:userAccess()};
this.findSubs = this.findSubs.bind(this);
}
componentDidMount(){
this.setState({subs:userAccess()});
}
findSubs(){
this.setState({subs:userAccess()});
}
render(){
return (
<div>
<Table>
<thead>
<tr>
<th>Subscription ID</th>
</tr>
</thead>
<tbody id="body">
{
this.state.subs.map((aSub) => (
<tr key ={aSub}>
<td key ={aSub}>{aSub.id}</td>
</tr>))
}
</tbody>
</Table>
</div>
)
}
}
export default MySubscriptions;
EDIT
This may not be the best way to do it, but it works
class MySubscriptions extends Component{
constructor(props){
super(props);
this.state ={subs:userAccess(), loaded:false};
}
async componentDidMount(){
await new Promise(resolve => { setTimeout(resolve, 500); });
this.setState({subs: await userAccess()}, () => {
this.setState({loaded:true});
return Promise.resolve();
});
}
render(){
return (
<div>
<Navbars/>
{this.state.loaded &&
<Table>
<thead>
<tr>
<th>Subscription ID</th>
</tr>
</thead>
<tbody id="body">
{
this.state.subs.map((aLine) => (
<tr>
<td>{aLine.id}</td>
</tr>
))
}
</tbody>
</Table>
}
</div>
)
}
}
export default MySubscriptions;
You could add a componentDidUpdate method.
In my opinion, the best practice is using the new Suspense component that was added with React 18, if you are using that version or higher.
You can read more about it and view code samples here.
If you have an earlier version, you can also use conditional rendering.

Not able to render the new component on onClick()

I am new to react and facing some problem while rendering a new component on onClick() on a table cell item.
class Component extends React.Component{
constructor(props) {
super(props);
this.routeChange = this.routeChange.bind(this)
this.state = {
values: []
};
}
routeChange(id) {
console.log(id)
const userAccount = (
<Account />
);
return userAccount;
}
render() {
return (
<div className="classname1">
<table>
<thead className="table-header">
<tr className="table-row">
<th>Account Name</th>
</tr>
</thead>
<tbody>
{this.state.values.map(value => {
return (
<tr className="data-table">
<td className="txt-blue" onClick={() => this.routeChange(value.id)}>{value.name}</td>
</tr>)
})}
</tbody>
</table>
</div>
}
So when I execute the above everything works fine and the table has been rendered properly but when I click on the table cell item then my component is not being rendered. But I can see the console.log() which I have passed in routeChange().
Note: My state values[] is not empty because as here I am only showing the snippet of my code.
You need to pass a reference of a function that calls routeChange function to the onClick function. One way to do this is to use an arrow function.
<td className="txt-blue" onClick={() => this.routeChange(values.value.id)}>{values.value.name}</td>
When you click and the event 'onClick' is triggered, it doesn't expect a return value, meaning that component you are returning is going nowhere.
What you can do to show the 'Account' component is keep a variable, say showAccount, in your state, which initialises as false, and with the method 'routeChange' what you do is change this to true.
I don't quite understand your use case, but something like this could be:
class Component extends React.Component{
constructor(props) {
super(props);
this.routeChange = this.routeChange.bind(this)
this.state = {
values: [],
accountId: null,
showAccount: false
};
}
routeChange(id) {
console.log(id)
/* Commenting this,
const userAccount = (
<Account />
);
return userAccount;
*/
this.setState({showAccount: true, accountId: id})
}
render() {
return (
<div className="classname1">
<table>
<thead className="table-header">
<tr className="table-row">
<th>Account Name</th>
</tr>
</thead>
<tbody>
{this.state.values.map(value => {
return (
<tr className="data-table">
<td className="txt-blue" onClick={() => this.routeChange(value.id)}>{value.name}</td>
</tr>)
})}
</tbody>
</table>
{this.state.showAccount && this.state.accountId &&
<Account id={this.state.accountId} />
}
</div>
}
Anyhow, try to play with your component and see what works best for you. What I suggest may not be useful for you, so just take the concept and adapt it for your own app.

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} />)}

Refreshing sorted table in React and state issue

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.

Resources