Callback or setState when path is loaded - reactjs

im trying to find an event or a function, that run everytime i loaded a component, i didn't want componentDidMount because it only get triggered once at the initial render, what i want is an event that run everytime the component gets rendered.i want to do this, because, i want to print an updated version of an array, but i need to use setState to trigger the re-rendering, is there any event or function that runs like this ?, i tried combining componentDidUpdate and componentDidMount but it just gave me an infinite loop
this is my home class to print the array :
export class Home extends React.Component{
constructor(){
super();
this.state = ({refresh: false})
this.refresh = this.refresh.bind(this)
}
render() {
return (
<Router>
<Switch>
<Route exact path="/home">
<div className="konten container-sm col-sm-10" >
<body>
<br></br><br></br>
<div className="tabel card rounded">
<div className="card-body">
<p className="head panel-body">User List</p>
<input type="text" className="search form-control col-sm-2" placeholder="Search user here">
</input>
<Link to="/add"><a href="" className="tambah btn" onClick={this.add}>Add User</a></Link>
<br/>
<table className="table">
<thead>
<tr>
<th scope="col">Nama</th>
<th scope="col">Username</th>
<th scope="col">Email</th>
</tr>
</thead>
{user_list.map(user =>
{
return(
<tbody>
<tr>
<td>{user.nama}</td>
<td>{user.username}</td>
<td>{user.email}</td>
<td><button className="edit btn">Edit</button></td>
</tr>
</tbody>
)
}
)}
</table>
</div>
</div>
</body>
</div>
</Route>
<Route path="/add">
<Tambah />
</Route>
</Switch>
</Router>
)
}
}
and here is my add class, to input a new data to the array :
save(event){
event.preventDefault()
user_list.push({
nama: this.state.nama ,
username: "John Doe",
email: this.state.company,
admin: this.state.admin,
status: this.state.active})
console.log(user_list)
this.setState({createNew: true})
}
note that this two code that i just posted is inside a different class, and i create a new data inside the array using push method, and when i get back to the home page, the data is not updated until i re-render the state, the question is, is there any function that can re-render every time i entered the home path ?
any help will be appreciated, thanks before

Use useEffect without dependencies it help's you
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}

If your function is dependent on the new props you'll be receiving from the parent that will render the component then you can use componentWillRecieveProps lifecycle. But if your re-render component is not dependent on the parent then you can simple add the array in you render and it will change on you re-render which will be base on the state.

Related

How to load all the urls in the component did mount and onclick should populate the table with state variables specific to the item selected?

Is there any way to load all the url at component did mount and update state variable. Onclick of particular item, state variable specific to that item should be displayed.Or is there any better way to do this ?
Only the contents of the table should be updated onclick.I have api for each of the item in the dropdown. Onclick of particular from the dropdown in tab, state variable should be updated based on selection and displayed in table. Is there any better way to do this ?
export class HomeComponent extends React.Component{
constructor(props){
super(props)
this.state={
version :[],
loading : true,
show : false
}
this.showVersion = this.showVersion.bind(this);
}
showVersion(vsg_component){
switch(vsg_component){
case 'option1':
var base_url = ""
case 'option2' :
var base_url = ""
}
this.call_function(base_url, version_url)
this.setState({
show : true
})
return false;
}
call_function(base_url, version_url){
fetch(base_url)
.then(response =>{
return response.json();
})
.then(data => {
this.setState({
version : data,
loading : false
})
})
.catch(error=>{
console.log(error);
})
}
// componentDidMount(){
// this.call_function();
//}
render(){
const contents = this.state.version.map((h,i)=>{
return <tr>
<td>{h.hostName}</td>
<td>{h.release}</td>
</tr>
})
return(
<div>
<div class="header">
<h1 class="main_header">VSG DASHBOARD</h1>
</div>
<div class="navbar">
<div class="dropdown">
<button class="dropbtn">VERSION
<i class="fa fa-caret-down"></i>
</button>
<div class="dropdown-content">
<a href="#" onClick ={() => this.showVersion('option1')}>VNF</a>
<a href="#" onClick ={() => this.showVersion('option2')}>CMTS</a>
CASSANDRA
KAFKA
KINESIS
</div>
</div>
<input type="text" name="searchbar" placeholder="search.." size="40"></input>
</div>
{ this.state.show ?(
<div className="version_table_component">
{ this.state.loading || !this.state.version ?
( <div class= "loader"> </div>
) :(
<table id="vnf_version" class="center">
<thead>
<tr>
<th>HOST</th>
<th>VERSIONS</th>
</tr>
</thead>
<tbody>
{contents}
</tbody>
</table>
)}
</div>): null }
</div>
) ;
}
}
Here I am using switch case in click event.call_function() function will receive url corresponding to the clicked item as the parameter and state variable is set which will be shown in the table. Please refer the screenshot.
[![screenshot][1]][1]
[1]: https://i.stack.imgur.com/NF6cO.jpg
Case 1:
You can put your URLs in array and loop through, then put response data to state by the key, for example setState({'CASSANDRA': response.data})
Then by click you store the Key: CASSANDRA, and render the data like this.state[this.state.currentDataKey]
Also you need to separate your html by components, then you could wrap for example Menu component in memo or use PureComponent to prevent unnecessary rerendering. Or <Table data={this.state[this.state.currentDataKey]}/>
Case 2:
If the response might change and you allways relevant data
You can create an object with keys and urls, like this: const urls = {'CASSANDRA': 'url...'}
then by click you put the Key to argument and make a request like request(urls[key]) and store it to state.tableData.
P.S. Don't forget to separate your code to smaller components

React looping over every value that a state variable has had before rendering on the final value

I have a class that listens for clicks on a map, and when a click has been detected, it is added to an array routeWayPoints:
import React, { useState } from "react";
import Tab from "./Tab";
import RouteWayPointsTable from "./RouteWayPointsTable";
import { map, removeFromTheMap, addToTheMap } from "../javascript/mapInterprater";
function RouteAnalysisTab() {
const [routeWayPoints, setRouteWayPoints] = useState([]);
function beginPlottingRoute() {
setRouteWayPoints([]);
}
map.on("click", onMapClick);
function onMapClick(event) {
if (isRouteActive) {
setRouteWayPoints(routeWayPoints.concat(event.latlng));
}
}
return (
<Tab id="analyse-route" title="Analyse route" className="data-display__tab-content">
<h3>Analyse route</h3>
{!isRouteActive && routeWayPoints.length > 0 && (
<button className="button" id="new-route-button" type="button" onClick={() => beginPlottingRoute()}>
Plot new route
</button>
)}
<div className="data-display__tab-content--route-analysis">
{!isRouteActive && routeWayPoints.length === 0 && (
<button className="data-display__button--home" id="plot-route-button" type="button" onClick={() => beginPlottingRoute()}>
Plot a route
</button>
)}
<RouteWayPointsTable routeWayPoints={routeWayPoints} />
</div>
</Tab>
);
}
export default RouteAnalysisTab;
I pass the array to the RouteWayPointTable component and attempt to loop over each item and create a row in the table using the RouteWayPointRow component.
import React from "react";
import RouteWayPointRow from "../components/RouteWayPointRow";
function RouteWayPointsTable(props) {
return (
<div id="route-analysis">
<table className="data-display__waypoint-table" id="analyse-route-waypoints">
<thead>
<tr>
<th className="data-display__waypoint-table--way-point-col">Way point</th>
<th className="data-display__waypoint-table--lat-col">Latitude</th>
<th className="data-display__waypoint-table--lng-col">Longitude</th>
<th className="data-display__waypoint-table--remove-col"></th>
</tr>
</thead>
<tbody>
{props.routeWayPoints.map((wayPoint, index) => {
return <RouteWayPointRow wayPoint={wayPoint} index={index} key={index} />;
})}
</tbody>
</table>
</div>
);
}
export default RouteWayPointsTable;
Now the rows are being displayed, but I'm observing some strange behaviour I didn't expect.
As I add points to the array, React is iterating over every state the array existed in before it renders the latest point, so the amount of time it takes to update the table is getting exponentially longer as it has to perform more loops over the array every time a new point has been added.
So my issue was that I had declared the map listener inside the component, and so a new listener was being created each time I clicked on the map which cause React to re-render the DOM
function RouteAnalysisTab() {
const [routeWayPoints, setRouteWayPoints] = useState([]);
function beginPlottingRoute() {
setRouteWayPoints([]);
}
map.on("click", onMapClick);
Probably not the most elegant solution, but removing the listener before creating it did the trick:
map.off("click");
map.on("click", onMapClick);

How to change the state in conditional - React

I need to change the state of sibling components in my React App.
I use state and setstate
I need to change the state of sibling components. When loading the page, there must exist (visible in the page) <BkUser /> and when clicking "button id =" ds-visual "it must be deleted (<BkUser /> mustn't exist) and there must exist <BkDescanso />.
When you click on <BkSleep /> (in the div parent) you should remove <BkDescanso /> and show <BkUser />
This is the web.
There should never be <BkUser/> and <BkSleep> at the same time. <Bkuser /> is the blue block and <BkDescanso /> is the red block
This is my code:
Edit: I edit my original code because I fix the problem. This is the final OK Code. In the end the most important thing was the state conditional
{
this.state.usuario ? (<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>): (<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>)}
import React, { Component } from 'react';
class Header extends Component {
constructor(props) {
super(props);
this.state = {
usuario: true,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
usuario: !state.usuario
}));
//alert("Works button");
}
render(){
return (
<header className="header">
<div className="configuracion">
{
this.state.usuario
? (
<BkUser handleClick = {this.handleClick} usuario={this.state.usuario}/>
)
: (
<BkDescanso handleClick = {this.handleClick} usuario={this.state.usuario}/>
)}
<div className="content-btn">
<button id="config" className='btn btn--rounded'><span className="ico-configuracion"></span></button>
<button id="salir" className='btn btn--rounded'><span className="ico-exit"></span></button>
</div>
</div>
</header>
);
}
}
class BkUser extends Component{
render(){
return ((
<div className='usuario'>
<img src="../img//usuario.svg" alt="Imagen usuario"/>
<div className="content-usuario">
<span id="nm-usuario" className="h4">Hermione Jane Granger</span>
<span id="tp-usuario" className="h5">Supervisor</span>
</div>
<div className="content-descansos">
<div className="botones">
<button id="ds-visual" className='btn btn--rounded' onClick={this.props.handleClick}><span className="ico-visual"></span></button>
<button id="ds-admin" className='btn btn--rounded'><span className="ico-tiempo-administrativo"></span></button>
<button id="ds-otros" className='btn btn--rounded'><span className="ico-descanso"></span></button>
</div>
<div className="ds-actual">
<span id="ds-tipo">Administrativo</span>
<span id="ds-tiempo">00:08:47</span>
</div>
</div>
</div>
));
}
}
class BkDescanso extends Component {
render(){
return ((
<div className='usuario descanso' onClick={this.props.handleClick}>
<h3>Finalizar descanso</h3>
</div>
));
}
}
export default Header;
Right now handleClick works but always exist BkUser and BkDescanso. I need only one to exist. If you click on id = "ds-visual" the bkUser block should disappear and BkDescanso appear. Then if you click on div className = 'user rest' in BkUser there should only be BkDescanso.
I think that it is not able to know when it is true and when it is false to show or hide
Thanks a lot for the help.
You're missing two things:
First you have to pass the handleClick function to the BkUser component, and then you have to call it via this.props.handleClick.
...
<BkUser handleClick={this.handleClick} usuario={this.state.usuario} />
....
<button
id="ds-visual"
className="btn btn--rounded"
onClick={this.props.handleClick}
>
ds-visual
<span className="ico-visual" />
</button>
CodeSandbox here.
Read more here.
You can change the state of the siblings by passing a function from the parent via props into them.
In the end your siblings are the children of their parent.
You can read this articles on how to change the state of child components.
React js change child component's state from parent component
https://medium.freecodecamp.org/react-changing-state-of-child-component-from-parent-8ab547436271
An other thing you could look into would be React Redux.

React router not rendering the component on click of a Array item

I'm creating a component which has array of items and showing them on one page using map function.
Need help on how I can show a detail view of a item by updating route value dynamically based on the item I clicked.
Currently when I click on a item know more though the url change to some wrong value I don't see any change in the page.
Component:
{location[area].map((item, index) =>
<div key={index} className="col-md-4 mt-3">
<div className="card">
<div className="card-body">
<h5 className="card-title">{item.title}</h5>
<p className="card-text">{item.description}</p>
<Link to={'/view/${item.title}'} onClick={() => addDetail(item)} className="btn btn-primary">Know more</Link>
</div>
</div>
</div>
)}
Router:
<Route path='/view/:id' exact component={DetailPage} />
DetailPage Component:
class DetailPage extends Component {
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}
Result Anchor Tag:
<a class="btn btn-primary" href="/view/${item.title}">Know more</a>
Onclick result url:
http://localhost:3000/view/$%7Bitem.title%7D
You need to use backticks for the to prop of your Link components so that template literals will be used and the variable will be inserted into the string.
<Link
to={`/view/${item.title}`}
onClick={() => addDetail(item)}
className="btn btn-primary"
>
Know more
</Link>
Well,As #tholle suggested use template literal.Your route seems fine, just make use of react life cycle method componentWillReceiveProps in the Detail Page Component.Here is the code
class DetailPage extends Component {
componentWillReceiveProps(nextProps){
if(this.props.match.params.id !== nextProps.match.params.id){
//make a call with updated nextProps.match.id or filter the existing data store
with nextProps.match.id
}
}
render() {
const selectedItem = this.props.selectedItem;
return(
<div>
<DetailedView selectedItem={this.props.selectedItem} />
</div>
);
}
}

React Dynamically using Component Names

I'm getting the following error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component />
external components
import Comment from 'material-ui-icons/Comment'
import Attachment from 'material-ui-icons/Attachment'
import History from 'material-ui-icons/History'
import TrendingUp from 'material-ui-icons/TrendingUp'
this.components = {
comment: Comment,
attachment: Attachment,
history: History,
trendingup: TrendingUp
}
render:
{this.menu.map((data, index) => (
<span key={index}>
{dynamicClassName = this.state[data.value.class]}
<span
key={index}
id="historicalTestsColorBandTitle"
className={"mobileBottomBannerOptions "+dynamicClassName}
onClick={this.handleBannerOptionsClick.bind(this, data.value.type)}
>
{DynamicComponentName = this.components[data.value.icon]}
{!dynamicClassName &&
<div>
<table><tbody><tr>
<td>
<DynamicComponentName
className="mobileBottomBannerIcon"
/>
</td>
</tr></tbody></table>
</div>
}
{dynamicClassName &&
<div>
<table><tbody><tr>
<td>
<DynamicComponentName
className="mobileBottomBannerIcon"
/>
</td>
<td className="menuOptionTitle">
{data.value.name}
</td>
</tr></tbody></table>
</div>
}
</span>
</span>
))}
You can see 'DynamicComponentName' is where the component name is created. I need to use a method like this as the order of these components changes depending on the last one clicked.
The components display fine but I get the error message at the top...
Any advice on how to resolve the error message?
thankyou!
You need to move your assignment out of JSX:
render() {
const dynamicClass = this.state[data.value.class];
const DynamicComponent = this.components[data.value.icon];
return // ...
}
If you need to make your assignment inside of map, you can do that too:
this.menu.map((data, index) => {
const dynamicClass = this.state[data.value.class];
const DynamicComponent = this.components[data.value.icon];
return // ...
})

Resources