2

I want to get dynamic content with React js modal I am using package react-responsive-modal. First I render all the post through map. Now I want when I click the individual post the modal should pop up and show me only that particular post's title and body. Now I can't figure out how to get an individual post in modal. Is it possible to do that via third-party package or I have to make custom modal for that?

import React from 'react';
    import Modal from 'react-responsive-modal';
    import Axios from 'axios';

    const styles = {
        fontFamily: 'sans-serif',
        textAlign: 'center'
    };

    class App extends React.Component {
        state = {
            posts: [],
            open: false
        };

        componentDidMount() {
            let url = 'https://jsonplaceholder.typicode.com/posts';
            Axios.get(url).then(res => {
                this.setState({
                    posts: res.data.slice(0, 10)
                });
                console.log(res.data.slice(0, 10));
            });
        }

        onOpenModal = () => {
            this.setState({ open: true });
        };

        onCloseModal = () => {
            this.setState({ open: false });
        };

        renderPosts() {
            return this.state.posts.map(post => {
                return (
                    <div
                        key={post.id}
                        style={{ width: 400, height: 400, backgroundColor: 'orange' }}
                        onClick={this.onOpenModal}
                    >
                        <h1>{post.title}</h1>
                    </div>
                );
            });
        }

        renderModal(id, title, body) {
            return this.state.posts.map(post => {
                return (
                    <div key={post.id} style={{ width: 400, height: 400, backgroundColor: 'orange' }}>
                        <h1>{post.id}</h1>
                        <h1>{post.title}</h1>
                        <p>{post.body}</p>
                    </div>
                );
            });
        }

        render() {
            const { open } = this.state;
            return (
                <div style={styles}>
                    <h2>react-responsive-modal</h2>

                    <div>{this.renderPosts()}</div>
                    <Modal open={open} onClose={this.onCloseModal} center>
                        <h2>Simple centered modal</h2>
                        <div>{this.renderModal()}</div>
                    </Modal>
                </div>
            );
        }
    }

    export default App;
Basit
  • 327
  • 3
  • 8
  • 19

4 Answers4

11

You'll need to introduce some additional state on your App component that keeps track of the currently selected post. In your onOpenModal() method, you can update that state with the index of the post that was clicked. Then, in renderModal(), you can check what the selected post is and only render that post instead of mapping over the entire array.

class App extends React.Component {
  state = {
    posts: [],
    open: false,
    selectedPost: null // Keep track of the selected post
  };

  componentDidMount() {
    let url = "https://jsonplaceholder.typicode.com/posts";
    Axios.get(url).then(res => {
      this.setState({
        posts: res.data.slice(0, 10)
      });
      console.log(res.data.slice(0, 10));
    });
  }

  onOpenModal = i => {
    this.setState({
      open: true,
      selectedPost: i // When a post is clicked, mark it as selected
    });
  };

  onCloseModal = () => {
    this.setState({ open: false });
  };

  renderPosts = () => {
    return this.state.posts.map((post, i) => {
      return (
        <div
          key={post.id}
          style={{ width: 400, height: 400, backgroundColor: "orange" }}
          onClick={() => this.onOpenModal(i)} // Pass the id of the clicked post
        >
          <h1>{post.title}</h1>
        </div>
      );
    });
  }

  renderModal = () => {
    // Check to see if there's a selected post. If so, render it.
    if (this.state.selectedPost !== null) {
      const post = this.state.posts[this.state.selectedPost];
      return (
        <div
          style={{ width: 400, height: 400, backgroundColor: "orange" }}
        >
          <h1>{post.id}</h1>
          <h1>{post.title}</h1>
          <p>{post.body}</p>
        </div>
      );
    }
  }

  render() {
    const { open } = this.state;
    return (
      <div style={styles}>
        <h2>react-responsive-modal</h2>

        <div>{this.renderPosts()}</div>
        <Modal open={open} onClose={this.onCloseModal} center>
          <h2>Simple centered modal</h2>
          <div>{this.renderModal()}</div>
        </Modal>
      </div>
    );
  }
}
ethan.roday
  • 2,485
  • 1
  • 23
  • 27
  • 1
    Why did you put a `key` prop on the div you are rendering? What would be different if you didn't do that? – Garrett Motzner Jan 23 '19 at 00:01
  • That’s unnecessary - I was just using your existing code and neglected to remove it. – ethan.roday Jan 23 '19 at 00:03
  • Updated the answer! – ethan.roday Jan 23 '19 at 00:04
  • 1
    Actually, I (who is not the OP, btw) would contend it _is_ necessary, at least in some cases. Even if you are not iterating over an element, providing a key prop can be useful, because it causes the entire element to be remounted, not just partially updated. This is sometimes desirable, and this modal might be one of those cases. If you don't use a key prop, react will try to reuse the same instances of as many DOM elements as possible. Sometimes you want that other times you don't. – Garrett Motzner Jan 23 '19 at 00:08
  • @GarrettMotzner that’s certainly true, and key props can be useful when you want to, say, reset the state of a particular component by forcing it to remount, but generally speaking React should be able to handle efficiently diffing and rendering in a case like this without any manual intervention. – ethan.roday Jan 23 '19 at 04:24
  • Did I answer the question you posed in your first comment, though? I’m a little confused, since you seemed to disagree with my answer :) – ethan.roday Jan 23 '19 at 04:25
  • you should update `renderModal(id, title, body)` to `renderModal()` – Sahil Raj Thapa Jan 23 '19 at 05:41
  • Good catch, @JSEngine - fixed. – ethan.roday Jan 23 '19 at 06:02
1

In the post onClick function set the post id/index in state along with the open flag

Inside the modal render use the saved index/id to pass that post to the modal as a param/prop.

You dont need to map over all posts inside the modal.

Sample

onOpenModal = (index) => {
   this.setState({ open: true, selectedPostIndex: index });
};

onCloseModal = () => {
   this.setState({ open: false, selectedPostIndex: undefined })
}

renderPosts() {
   return this.state.posts.map((post, index) => {
      return (
         <div key={post.id} onClick={() => this.onOpenModal(index)}>
            <h1>{post.title}</h1>
         </div>
      )
   })
}

render() {
....
  <Modal open={open} onClose={this.onCloseModal} center>
    <h2>Simple centered modal</h2>
    <div>{this.renderModal(this.state.posts[this.state.selectedPostIndex])}</div>
  </Modal>
 ....
}

renderModal(post) {
   return (
      <div key={post.id} style={{ width: 400, height: 400, backgroundColor: 'orange' }}>
         <h1>{post.id}</h1>
         <h1>{post.title}</h1>
         <p>{post.body}</p>
      </div>
   )
}
varoons
  • 3,807
  • 1
  • 16
  • 20
0

Using React Hooks

Create a modal with dynamic props like this

export default function Modal({
  open,
  setOpen,
  onConfirm,
  title,
  description,
  confirmText,
}) 

Then render the component. i did it like this

const getModal = () => {
    return (
        <Modal open={open} setOpen={setOpen} title={title} description={description} confirmText={confirmText} onConfirm={confirmAction} />
    )
}

and then when you want to display your dynamic modal use a function like this ConfirmAction can not be a function you should call the function inside that Modal according to that confirmation

const createModal = (title, description, confirmText, confirmAction) => {
    setTitle(title);
    setDescription(description);
    setConfirmText(confirmText);
    setConfirmAction(confirmAction);
    setOpen(true);
}
MORÈ
  • 2,480
  • 3
  • 16
  • 23
-1

Initialize your state with one array which will hold

state = {
        posts: [],
        open: false,
        modalShow: [false,false,false,false,false,false,false,false,false,false] // this is 10 as you have only 10 posts
    };

Now modify render posts

    onOpenModal = (id) => {
        const newModalShow = [...this.state.modalShow];
        newModalShow[id] = true; 
        this.setState({ modalShow: newModalShow});
    };


    renderPosts() {
        return this.state.posts.map((post,index) => {
            return (
                <Fragement>
                <div
                    key={post.id}
                    style={{ width: 400, height: 400, backgroundColor: 'orange' }}
                    onClick={()=>this.onOpenModal(index)}
                >
                    <h1>{post.title}</h1>
                </div>
                <Modal open={this.state.modalShow[index]} onClose={this.onCloseModal} center>
                    <h2>post.title</h2>
                    <div>{this.renderModal()}</div>
                </Modal>
               <Fragement>
            );
        });
    }
Anil Kumar
  • 2,223
  • 2
  • 17
  • 22
  • Why would one want to render all possible modals but keep them hidden, rather than just rendering the active modal, and changing it when the state changes? Also, would there be a better way to track each individual post's visible state, rather than a static length array? – Garrett Motzner Jan 22 '19 at 23:56