How to cancel mutlicall of componentDidMount() in ReactJS component? - javascript

I have created a component DATA which has componentDidMount() function to get data from the API. After this, I import it into the main component. The main component has a render method where I have a simple structure: 1st div - Some info and imported component DATA, 2nd div is a button which opens a modal, which has some text and button which closes this modal dialog box.
I have read that componentDidMount() is called only once after the component is rendered.
But when I push button to open my modal page at the same time componentDidMount() is called again.
What I need is that componentDidMount() don't run when I open the modal box. But run only when the page is rendered or refreshed.
main component
import React from "react";
import Modal from '../components/modal/form'
import Actions from '../data/Actions'
class MainPage extends React.Component{
constructor(){
super();
this.state = {
show: false,
};
this.showModal = this.showModal.bind(this);
//this.setSearchTopStories = this.setSearchTopStories.bind(this);
};
showModal = e => {
this.setState({
show: !this.state.show
});
};
render(){
return <div>
<div className="topDescribtion">
<h2>descr</h2>
<Actions />
</div>
<div className="btnNewTransaction">
<button onClick={e => {
this.showModal();
}}>
show Modal
</button>
<Modal onClose={this.showModal} show={this.state.show}>
Mdl--
</Modal>
</div>
<div className="transactionList"></div>
</div>
}
}
export default MainPage;
DATA component
import React, { Component } from "react";
import Modal from '../components/modal/form'
const PATH_BASE = 'my URL which I give data in JSON format and it works fine';
class Actions extends React.Component{
constructor(){
super();
this.state = {
result:null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
}
setSearchTopStories(result) {
this.setState({ result });
};
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
};
render(){
const { searchTerm, result } = this.state;
console.log(result);
return <div></div>;
}
}
export default Actions;

The componentDidMount() method in Actions component is being called again as your parent component is re-rendered and so will the child. To stop this make your Actions component a Pure component instead.
A Pure component is only re-rendered when there is a change in state or props of that component.
Replace
class Actions extends React.Component{
with
class Actions extends React.PureComponent {
Hope it helps!!

Related

index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application

This was my code
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
invites: [],
};
constructor() {
super();
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
state and setState have worked for me alright for more complex codes before. This one keeps showing the same error
This is the error:
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the App component.
Add a componentDidMount() and write your request call inside it. When the component first loads the componentDidMount function will run.
Actually you can make request in constructor (React allows it but you shouldnt) but you have to make the request only after the component has been mounted or just before it is about to be mounted.
So it is wise to make your requests in componentDidMount().
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
constructor(props) {
super(props);
this.state = {
invites: [],
};
}
componentDidMount() {
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;

Using button in one component to render another component in main (App) component

I am trying to display/hide one component which is ItemMain and which is imported to the main App component using button in another component which is NavLogoNew. I tried to do this in many different ways but it looks like the button doesn't know if it's clicked, when I change true/false manually it works. In web I found a lot of stuff about situations when only two components are involved, but nothing like this. My code:
App
import React from 'react';
import './App.css';
import { tsPropertySignature } from '#babel/types';
import { statement } from '#babel/template';
import NavBar from './../Components/Navigation/NavBar/NavBar.js';
import ItemMain from './../Components/Item/ItemMain/ItemMain.js';
import ItemList from './../Components/Item/ItemList/ItemList.js';
import NavButtonTop from './../Components/Navigation/NavButton/NavButtonTop/NavButtonTop.js';
import NavLogoNew from './../Components/Navigation/NavButton/NavButtonNew/NavLogoNew.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
visible: !this.visible
})
}
render() {
return (
<div className="App">
<NavBar />
{this.state.visible ? <ItemMain /> : null}
<ItemList />
<NavButtonTop name='UP'/>
</div>
);
}
}
export default App;
NavLogoNew:
import React from 'react';
import './NavLogoNew.css';
import ItemMain from './../../../Item/ItemMain/ItemMain.js'
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={this.props.click}
>
{this.props.name}
</button>
);
}
}
export default NavLogoNew;
Your handleClick function is lacking something
use !this.state.visible so change from the below
handleClick(){
this.setState({
visible: !this.visible
})
}
to
handleClick = () => {
this.setState({
visible: !this.state.visible
})
}
pass the handleClick function to the NavLogoNew as follows
<NavLogoNew onClick = {this.handleClick} />
inside of the NavLogoNew component you should invoke it as follows
class NavLogoNew extends React.Component {
render() {
return (
<button
className='NavLogoNew'
onClick={() => this.props.onClick()}
>
{this.props.name}
</button>
);
}
}

How to show the identifier on the page?

I used react.js to connect the api.
And i want to show the identifier on the page.
Here is response to console.log(this.state.weathers.cwbopendata)
After i console.log(this.state.weathers.cwbopendata.identifier)
,I got the error
What should i do to show the identifier on the page?
Here is the code:
import React,{Component} from 'react';
class App extends Component {
constructor(){
super();
this.state = {
weathers: {},
};
}
componentDidMount(){
fetch('https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON')
.then(response=> response.json())
.then( JSON=> this.setState({weathers:JSON}))
}
render(){
return (
<div className="App">
{console.log(this.state.weathers.cwbopendata.identifier)}
</div>
);
}
}
export default App;
This is a classic problem many newcomers face. You need to add a state to let your component know that data fetching is in progress, is completed or there is an error. So the component can show real data when it successfully fetched it, until then something you can display to the UI to let users know that the App is fetching data... I'd write it something like:
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
weathers: {},
isFetching: true
};
}
componentDidMount() {
fetch(
"https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON"
)
.then(response => response.json())
.then(json => this.setState({ weathers: json, isFetching: false }));
}
render() {
const { isFetching, weathers } = this.state;
return (
<div className="App">
{isFetching ? "Loading.." : weathers.cwbopendata.identifier}
</div>
);
}
}
export default App;
In your case you tried to render the data at first mount, and at this point of time weathers is just holding an empty object {}. Thus weathers.cwbopendata returns undefined, and undefined.identifier throws the error as you see in the browser console.
Initially render method is being called before componentDidMount.
By the time it was called, data hadn't been fetched yet.
So you should properly handle situation when you have empty state.
import React,{Component} from 'react';
class App extends Component {
constructor(){
super();
this.state = {
weathers: {},
};
}
componentDidMount(){
fetch('https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/F-C0032-001?Authorization=CWB-BB78764B-9687-4C1C-B180-66CB616129E5&format=JSON')
.then(response=> response.json())
.then( JSON=> this.setState({weathers:JSON}))
}
render(){
return (
<div className="App">
{console.log(this.state.weathers.cwbopendata && this.state.weathers.cwbopendata.identifier)}
</div>
);
}
}
export default App;

How do I close upon button click of a rendered component in react?

I'm having trouble figuring out how to close a component that's rendered. I currently have am able to open the modal component on my first page, but then I want to close it upon the click of a button through the component. How would I do that?
import React, { Component } from 'react';
import AddModal from './addmodal';
class Page extends Component {
constructor(){
super();
this.state = { isModalOpen: false };
}
...//skip
handleAdd= () =>{
this.setState({ isModalOpen: true });
}
render(){
return (
<div>
<button onClick={this.handleAdd} > Add </button>
<AddModal isOpen={this.state.isModalOpen} />
</div>
)
}
}
import React, { Component } from 'react';
class AddModal extends Component {
// ... skip
handleClose = () => {
this.setState({ open: false });
};
render(){
return(
<modal inOpen={this.props.isOpen} >
<Button onClick={this.handleClose}>
Okay
</Button>
...//skip
</modal>
)
}
}
export default AddModal;
You need to make your modal component call an onClose callback so the parent can close it:
import React, { Component } from 'react';
import AddModal from './addmodal';
class Page extends Component {
constructor(){
super();
this.state = { isModalOpen: false };
}
...//skip
handleAdd= () =>{
this.setState({ isModalOpen: true });
}
handleClose= () =>{
this.setState({ isModalOpen: false });
}
render(){
return (
<div>
<button onClick={this.handleAdd} > Add </button>
<AddModal isOpen={this.state.isModalOpen} handleClose={this.handleClose}/>
</div>
)
}
}
import React, { Component } from 'react';
class AddModal extends Component {
// ... skip
render(){
return(
<modal inOpen={this.props.isOpen} >
<Button onClick={this.props.handleClose}> // call to parent
Okay
</Button>
...//skip
</modal>
)
}
}
export default AddModal;
The parent component will decide whether the modal is open or not, hence it owns the state of the child.
Building on what #jsdeveloper posted, I think you can cut the clutter by using the same function to handle the opening and closing of the modal.
We will make use of a toggleModal function which changes the isModalOpen to true when it's false and vice versa. It uses a callback, which takes in the previous state value of isModalOpen.
toggleModal = () => {
this.setState(prevState => ({
isModalOpen: !prevState.isModalOpen
}))
}
So the final file should look something like this.
import React, { Component } from 'react';
import AddModal from './addmodal';
class Page extends Component {
constructor() {
super();
this.state = {
isModalOpen: false
};
}
// ... //skip
toggleModal = () => {
this.setState((prevState) => ({
isModalOpen: !prevState.isModalOpen
}));
};
render() {
return (
<div>
<button onClick={this.handleAdd}> Add </button>{' '}
<AddModal
isOpen={this.state.isModalOpen}
toggleModal={this.toggleModal}
/>
</div>
);
}
}
import React, { Component } from 'react';
class AddModal extends Component {
// ... skip
render() {
return (
<modal inOpen={this.props.isOpen}>
<Button onClick={this.props.toggleModal}>Close</Button>
//... skip
</modal>
);
}
}
export default AddModal;

How to prevent browser from fetching the same href repeatedly

I am a freshman in react ,I want to write a react component of getting the member info of a team by teamId.
React code
import React from 'react';
import PropTypes from 'prop-types';
import UserTable from './pm_user_table';
import {Form,Modal,Input,Button} from 'antd';
const FormItem = Form.Item;
class PMBody extends React.Component{
constructor(props){
super(props);
this.state={
curTeam:this.props.curTeam,
memberList:[]
}
}
componentWillMount(){
console.log('component mount');
}
componentWillReceiveProps(nextProps){
if(nextProps.curTeam !== this.state.curTeam){
this.setState({curTeam:nextProps.curTeam});
}
}
render(){
let {getFieldProps} = this.props.form;
const teamId = this.state.curTeam;
var myFetchOptions={method: 'GET'};
fetch("http://localhost:3001/teamMembers/" +this.state.curTeam,myFetchOptions)
.then(response=>response.json())
.then(json => {
this.setState({memberList:json});
}
).catch(function(){
console.log("error");
});
let memberList = this.state.memberList;
const body = memberList !='' ?
<UserTable dataSource={memberList} actions={this.props.actions} />
:
''
;
return (
<div>
{body}
</div>
)
}
PMBody.PropTypes = {
curTeam:PropTypes.string.isRequired,
actions: PropTypes.object.isRequired
}
export default PMBody =Form.create({})(PMBody);
By the network view in chrome devtool,It seems that the browser request the same url repeatedly.
So why it fetch the same url repeately?
You're misunderstanding the purpose of the render() method.
React calls render() to update your component anytime anything changes. It must be pure and should not interact with anything else.
You should move that to componentDidMount().

Categories

Resources