Tomasz
Tomasz

Reputation: 5152

Accessing and showing modal located in another component

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 a state = {}; 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

Answers (2)

Ahmed Khattab
Ahmed Khattab

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

Akber Iqbal
Akber Iqbal

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,

  • Instead of: <List.Item onClick={{this.showModal(123)} style={{cursor: 'pointer'}}>
  • We do: <List.Item onClick={() => {this.showModal(123)} } style={{cursor: 'pointer'}}>

updated stackblitz here

Upvotes: 1

Related Questions