Reputation: 5152
I have a PersonList
component, wherever user clicks on List.Item
I want to pass some details like personId
to PersonModal
and display it. I'm using Ant Design component for modal.
Below is what I tried, but unfortunately I'm getting an error like:
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 astate = {};
class property with the desired state in the PersonModal component.
PersonModal.js
import React from "react";
import {Modal} from "antd";
class PersonModal extends React.Component {
constructor(props) {
super(props);
console.log(this.props.personId)
}
state = { visible: false };
showModal = () => {
this.setState({
visible: true,
});
};
// handleOk (...)
// handleCancel (...)
render() {
return <Modal
title="Basic Modal"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
Modal body
</Modal>
}
}
export default PersonModal;
PersonList.js
import React from "react";
import {Icon, Input, List, Avatar} from "antd";
import PersonModal from "PersonModal/PersonModal"
class PersonList extends React.Component {
showModal(personId) {
const modal = new PersonModal({personId: 123})
modal.showModal()
}
render() {
const persons = this.props.list;
return (
<div>
<List
itemLayout="horizontal"
dataSource={persons}
renderItem={item => (
<>
<List.Item onClick={this.showModal(123)} style={{cursor: 'pointer'}}>
<List.Item.Meta
avatar={<Avatar src="avatar.png"/>}
title={<span>{item.firstName} {item.lastName}</span>}
description={<span>{item.city}, {item.country}</span>}
/>
</List.Item>
</>
)}
/>
</div>
);
}
}
What would be correct way of approaching this problem? Since I am new to React i suppose this is not a correct approach.
Reproduced issue on stackblitz here
Upvotes: 0
Views: 1226
Reputation: 2799
you need to track the visible state of the PersonModal in the PersonList component. You should have a boolean variable to control the visibility of the PersonModal.
and the PersonModal will not control its visiblity state, rather it will get it from its clients , in your case the client is PersonList. so lets begin with the code
first, edit the PersonModal component to expect props from its clients
class PersonModal extends React.Component {
// handleOk (...)
// handleCancel (...)
handleCancel = () => {
// because the client controls the visivlity state of the component
this.props.hideModal();
}
render() {
/// this.props.isVisible is required, and it will be a boolean true or false.
const shouldBeVisible = this.props.isVisible;
return <Modal
title="Basic Modal"
visible={shouldBeVisible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
Modal body
</Modal>
}
}
export default PersonModal;
so now your PersonModal component expexts a prop; which is isVisible prop.
class PersonList extends React.Component {
state = {
// the client controls the visibility of the modal with this state key;
showModal: false,
personId: null
}
// edited
showModal = (personId) => {
// set state to update the UI and show the PersonModal
this.setState({
showModal: true,
personId: personId
});
}
hideModal= () => this.setState({showModal: false});
render() {
const persons = this.props.list;
return (
<div>
// now PersonModal will only be visible when the parent of it tells it to
<PersonModal
isVisible = {this.state.showModal}
hideModal= {this.hideModal}
/>
<List
itemLayout="horizontal"
dataSource={persons}
renderItem={item => (
<>
<List.Item onClick={() => this.showModal(123)} style={{cursor: 'pointer'}}>
<List.Item.Meta
avatar={<Avatar src="avatar.png"/>}
title={<span>{item.firstName} {item.lastName}</span>}
description={<span>{item.city}, {item.country}</span>}
/>
</List.Item>
</>
)}
/>
</div>
);
}
}
hope it helps you;
this is how the problem is approached in the React world
Upvotes: 1
Reputation: 15041
Your onClick handlers are being called continuously. Change them to functions that return the function you wish to call and this should fix your issue.
So,
<List.Item onClick={{this.showModal(123)} style={{cursor: 'pointer'}}>
<List.Item onClick={() => {this.showModal(123)} } style={{cursor: 'pointer'}}>
updated stackblitz here
Upvotes: 1