Not able to render the new component on onClick() - reactjs

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.

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.

Display fetched data into table in React

I am unable to tel why my table has no data. Is it the way I'm calling for the display, {this.newItems} or what could it be?
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
taskList: []
};
}
componentDidMount() {
this.refreshList();
}
//refreshList Function start
refreshList = () => {
axios
.get("http://127.0.0.1:8000/list/api/rooms/")
.then(res => this.setState({ taskList: res.data }))
.catch(err => alert(err))
}
//refreshList Function end
//renderItems Function start
renderItems = () => {
const newItems = this.state.taskList;
return newItems.map(item => (
<tr key={item.id}>
<td>{item.thing1}</td>
<td>{item.thing2}</td>
<td>{item.thing3}</td>
</tr>
));
};
//renderItems Function end
render (){
return (
<div>
<Navbar/>
<table className="layout container">
<thead>
<tr>
<th>thing1</th>
<th>thing2</th>
<th>thing3</th>
</tr>
</thead>
<tbody>
{this.newItems}
</tbody>
</table>
<Footer/>
</div>
)
}
}
The reason is that inside <tbody> tag you are using this.newItems, which is a local variable and doesn't exist outside the scope of renderItems function.
The piece of code responsible to render the table data is renderItems function, which you created but never called.
So you just need to do that:
<tbody>
{this.renderItems()}
</tbody>
Also, inside the renderItems function, there is no need to create additional variable if you are not going to modify it. So it can be written simply as:
renderItems = () => {
return this.state.taskList.map(item => (
<tr key={item.id}>
<td>{item.thing1}</td>
<td>{item.thing2}</td>
<td>{item.thing3}</td>
</tr>
));
};

Creating a component in react and typescript

I am trying to convert this table below from react to typescript. I want to make it universal table component where I can use for every data. This is a sample a table I created in react and its working well. In typescript, the class experts an interface which I have been finding it difficult to apply here.The table must be able to accept data from a webservice and display at any point in time. What am I supposed to add.
class Table extends Component {
render() {
const { characterData, removeCharacter } = this.props;
return (
<table className="table-bordered table-striped">
<TableHeader />
<TableBody characterData={characterData}
removeCharacter={removeCharacter}
/>
</table>
);
}
}
const TableHeader = () => {
return (
<thead>
<tr>
<th width="300">Name</th>
<th width="300">Job</th>
<th width="300">Date</th>
</tr>
</thead>
);
}
const TableBody = props => {
const rows = props.characterData.map((row, index) => {
return (
<tr key={index}>
<td >{row.name}</td>
<td >{row.job}</td>
<td >{row.date}</td>
<td><button onClick={() => props.removeCharacter(index)}>Delete</button></td>
</tr>
);
});
return <tbody>{rows}</tbody>;
}
export default Table;
You can type check your props by adding chevrons next to the Component prototype. The first argument is type checking your props and the second argument will type check your internal state.
interface TableProps {
characterData: //...however the data should be structured
removeCharacter: () => void;
}
interface TableState {
//... this is empty because you haven't defined any internal component state
}
class Table extends Component<TableProps, TableState> {
render() {
const { characterData, removeCharacter } = this.props;
return (
<table className="table-bordered table-striped">
<TableHeader />
<TableBody characterData={characterData}
removeCharacter={removeCharacter}
/>
</table>
);
}
}

Prevent all children re-rendering every call of setState

When rendering a long list of elements in a table for example, then any call to setState regardless of whether it changes the data the table uses for enumeration results in a re-render of every child object.
Every check/uncheck will re-render every element. This causes major slowdown with 2000 elements that are more complex. I will use virtualization, but want to make sure this is as performant as possible before doing so.
class App extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
const rows = Array.from({ length: 200 }, (v, k) => k + 1).map(i => ({
id: i,
field1: "hello",
field2: "cruel",
field3: "world"
}));
this.state = {
selectedIds: [],
rows
};
}
onChange(e) {
const name = e.target.name;
const checked = e.target.checked;
const selectedIds = [...this.state.selectedIds];
if (!checked) {
const index = selectedIds.findIndex(x => x === name);
selectedIds.splice(index, 1);
} else {
selectedIds.push(name);
}
this.setState(state => ({ selectedIds }));
}
render() {
const { rows, selectedIds } = this.state;
return (
<div className="App">
<h5>{selectedIds.length} Rows Selected</h5>
<table>
<thead>
<tr>
<td>Select</td>
</tr>
<tr>
<td>Field 1</td>
</tr>
<tr>
<td>Field 2</td>
</tr>
<tr>
<td>Field 3</td>
</tr>
</thead>
<tbody>
{rows.map(row => {
console.log(row);
return (
<tr key={row.id}>
<td>
<input
type="checkbox"
onChange={this.onChange}
name={row.id}
/>
</td>
<td>
<div>{row.field1}</div>
</td>
<td>
<div>{row.field2}</div>
</td>
<td>
<div>{row.field3}</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
I see from other answers that it is the expected behaviour to re-run the render function
ReactJS - Does render get called any time "setState" is called?
But if so, when the number of elements is increased to say 4000 in that link, why is there so much slowdown when clicking? Significant slowdown is noticed when only using 200 items when using slightly more complex custom React Components.
To solve this issue you'll want to create an additional component for the row item. This way it will have a separate render function and not rerender itself because the row component hasn't changed, only the table component.
{rows.map(row => {
return (
<RowItem row={row} onChange={this.onChange}>
);
}}
and your new component will look something like this:
class RowItem extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<tr key={this.props.row.id}>
<td>
<input
type="checkbox"
onChange={this.props.onChange}
name={this.props.row.id}
/>
</td>
<td>
<div>{this.props.row.field1}</div>
</td>
<td>
<div>{this.props.row.field2}</div>
</td>
<td>
<div>{this.props.row.field3}</div>
</td>
</tr>
);
}
}

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