skip to Main Content

I currently have a Material UI Modal component added to my react/typescript project but I’m having some issues that I can’t figure out. So on my current site, if you click on one of the card elements, it should open up a modal view with expanded information. However, I’m having an issue that when you click any card element, it only shows the most recently added information. I’m not exactly sure why this is happening.

This is the page:

page

this is after I click on "test" by "test12" (clearly showing the wrong information):

page after click

and this is the code for the page:

import * as React from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router'
import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import Copyright from "../assets/copyright";
import { CardActionArea, Modal } from '@mui/material';

const theme = createTheme();

const modalStyle = {
    position: 'absolute' as 'absolute',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    width: 1000,
    bgcolor: '#19191a',
    color: "white",
    border: '2px solid #000',
    borderRadius: "30px",
    boxShadow: 24,
    p: 4,
};

export default function Album() {
    console.log("LOCAL STORAGE IN FEED:")
    console.log(localStorage.getItem('user'))

    const [posts,setPosts] = React.useState([]);
    const user = JSON.parse(localStorage.getItem('user')!);
    const postsRef = React.useRef(null);
    const navigate = useNavigate();
    const refreshPage = () => {
        if (localStorage.getItem('refreshed') === 'false') {
            localStorage.setItem('refreshed', 'true');       
            navigate(0);
        }
    }
    
    
    React.useEffect(() => {
        refreshPage();
        axios.get(`http://127.0.0.1:8000/service/authors/${user.id}/posts/`).then(
            (response) => { 
                console.log("GET POSTS IN FEED RESPONSE:", response);
                postsRef.current = response.data.items;
                setPosts(response.data.items);
                
        }
        )
    },[]);

    const [open, setOpen] = React.useState(false);
    const handleOpen = () => setOpen(true);
    const handleClose = () => setOpen(false);

    return (
        <ThemeProvider theme={theme}>
            <Container sx={{ pt: 5 }}>
                <main>
                    <Box sx={{ borderBottom: 1, borderColor: 'grey.500' }}>
                        <Container maxWidth="lg">
                            <Stack
                                sx={{ pb: 2 }}
                                direction="row"
                                spacing={15}
                                justifyContent="center"
                            >
                                <Stack
                                    sx={{ pt: 4 }}
                                    direction="column"
                                    spacing={0}
                                    justifyContent="center"
                                >
                                    <Typography
                                        component="h1"
                                        variant="h2"
                                        align="left"
                                        color="text.primary"
                                        pt={8}
                                        gutterBottom
                                    >
                                        Welcome {user.displayName || "User"}!
                                    </Typography>
                                    <Typography variant="h6" align="left" paddingLeft={5} color="text.secondary" paragraph>
                                        This is your <em>dashboard</em>. View public posts here or publish your own!
                                    </Typography>
                                </Stack>
                                <Stack
                                    sx={{ pt: 20 }}
                                    direction="column"
                                    spacing={2}
                                    justifyContent="center"
                                >
                                    <Button variant="contained" href='./NewPost' startIcon={<AddIcon />}>New Public Post</Button>
                                </Stack>
                            </Stack>
                        </Container>
                    </Box>


                    <Container sx={{ py: 8 }} maxWidth="lg">
                        {/* End hero unit */}
                        <Grid container spacing={4}>
                            {posts.map((post) => (
                                <Grid item key={post} md={3}>
                                    <Card
                                        sx={{ height: "100%", display: 'flex', flexDirection: 'column', maxHeight: "390px"}}
                                        variant="outlined"
                                        color='#09bef0'
                                    >
                                        <CardActionArea
                                            onClick={handleOpen}>
                                            <CardContent sx={{ flexGrow: 1, bottom: "2px" }}>
                                                <Typography gutterBottom variant="h6" component="h1">
                                                    <b>{post.title}</b>
                                                </Typography>
                                                <Typography gutterBottom variant="subtitle2" component="h3">
                                                    {post.author.displayName}
                                                </Typography>
                                                <Typography gutterBottom variant="subtitle1" component="h3">
                                                    {post.description}
                                                </Typography>
                                            </CardContent>
                                            <CardMedia
                                                component="img"
                                                sx={{
                                                    // 16:9
                                                    pt: '0%',
                                                }}
                                                // image="https://source.unsplash.com/random"
                                                image="https://imgs.search.brave.com/QN5ZdDJqJOAbe6gdG8zLNq8JswG2gpccOqUKb12nVPg/rs:fit:260:260:1/g:ce/aHR0cHM6Ly93d3cu/YmlpYWluc3VyYW5j/ZS5jb20vd3AtY29u/dGVudC91cGxvYWRz/LzIwMTUvMDUvbm8t/aW1hZ2UuanBn"
                                                alt="random"
                                            />
                                        </CardActionArea>
                                        <Modal
                                            open={open}
                                            // sx={{overflow: scroll}}
                                            onClose={handleClose}
                                            aria-labelledby="modal-modal-title"
                                            aria-describedby="modal-modal-content"
                                        >
                                            {/* <div> */}
                                            <Box sx={modalStyle}>
                                                    <Stack
                                                        sx={{ pb: 2}}
                                                        direction="row"
                                                        spacing={20}
                                                        // justifyContent="center"
                                                    >
                                                        <Stack
                                                            sx={{ pb: 2 }}
                                                            direction="column"
                                                            spacing={6}
                                                            justifyContent="center"
                                                            marginRight={10}
                                                            paddingLeft={3}
                                                        >
                                                            {/* <Container> */}
                                                                <Typography id="modal-muthor" variant="h6" component="h2" paddingBottom={2}>
                                                                    Post Author: <em>{post.author.displayName}</em>
                                                                </Typography>
                                                                <Typography id="modal-title" variant="h4" component="h2">
                                                                    <b>{post.title}</b>
                                                                </Typography>
                                                                <Typography id="modal-modal-content" sx={{ mt: 2 }}>
                                                                    {post.content}
                                                                </Typography>
                                                            {/* </Container> */}
                                                        </Stack>
                                                        <Box
                                                            component="img"
                                                            sx={{height: 300,
                                                            width: 300}}
                                                            // display="flex"
                                                            // justifyItems="right"
                                                            alt="Post Picture"
                                                            src="https://imgs.search.brave.com/QN5ZdDJqJOAbe6gdG8zLNq8JswG2gpccOqUKb12nVPg/rs:fit:260:260:1/g:ce/aHR0cHM6Ly93d3cu/YmlpYWluc3VyYW5j/ZS5jb20vd3AtY29u/dGVudC91cGxvYWRz/LzIwMTUvMDUvbm8t/aW1hZ2UuanBn"
                                                        />
                                                    </Stack>
                                                </Box>
                                            {/* </div> */}
                                        </Modal>
                                    </Card>
                                </Grid>
                            ))}
                        </Grid>
                    </Container>
                </main>


                {/* Footer */}
                <Box sx={{ bgcolor: 'background.paper', p: 6 }} component="footer">
                    <Typography variant="h6" align="center" gutterBottom>
                        Footer
                    </Typography>
                    <Typography
                        variant="subtitle1"
                        align="center"
                        color="text.secondary"
                        component="p"
                    >
                        Something here to give the footer a purpose!
                    </Typography>
                    <Copyright />
                </Box>
                {/* End footer */}
            </Container>
        </ThemeProvider>
    );
}

2

Answers


  1. You have the Modal logic inside the map, so you are creating 3 modals that open when open is true. To fix this you need first to hold in the state the selected post:

    const [selectedPost, setSelectedPost] = React.useState(null);
    

    This will hold the selected post when you click on one of the cards. The handleOpen have to change a bit too:

    const handleOpen = (clickedPost) => {
        setOpen(true);
        setSelectedPost(clickedPost);
    }
    

    You also have to change the onClick on CardActionArea to something like this:

    onClick={()=>handleOpen(post)}
    

    And Finally put the Modal component outside the map and make the next changes where the data of selected post appears:

    <Typography id="modal-muthor" variant="h6" component="h2" paddingBottom={2}>
        Post Author: <em>{selectedPost?.author?.displayName ?? 'No author'}</em>
    </Typography>
    <Typography id="modal-title" variant="h4" component="h2">
        <b>{post?.title ?? 'No title'}</b>
    </Typography>
    <Typography id="modal-modal-content" sx={{ mt: 2 }}>
        {post?.content ?? 'No content'}
    </Typography>
    
    Login or Signup to reply.
  2. Because when you click a card to open its modal, actually you’ve opened all modals, because all modals open with the same state open.

    So, what you have to do:

    • First, you have to get modal out of mapping to avoid repeating it in DOM with every item.
    • Secondly, you’ll update open state to open modal with specific item.
    const [selectedItem, setSelectedItem] = React.useState(null);
    const handleOpen = (item) => setOpen(item);
    const handleClose = () => setOpen(null);
    
    return (
       <Container>
          <Grid container spacing={4}>
             {posts.map((post) => (
                <Grid item key={post.id} md={3}>
                   <Card
                      sx={{ height: "100%", display: 'flex', flexDirection: 'column', maxHeight: "390px"}}
                      variant="outlined" color='#09bef0'
                   >
                      <CardActionArea onClick={handleOpen}>
                         <CardContent sx={{ flexGrow: 1, bottom: "2px" }}>
                            <Typography gutterBottom variant="h6" component="h1">
                               <b>{post.Title}</b>
                            </Typography>
                            <Typography gutterBottom variant="subtitle2" component="h3">
                               {post.author.displayName}
                            </Typography>
                            <Typography gutterBottom variant="subtitle1" component="h3">
                               {post.Description}
                            </Typography>
                         </CardContent>
                      <CardMedia
                         component="img" alt="random"
                         sx={{ pt: '0%'}}
                         image="https://imgs.search.brave.com/QN5ZdDJqJOAbe6gdG8zLNq8JswG2gpccOqUKb12nVPg/rs:fit:260:260:1/g:ce/aHR0cHM6Ly93d3cu/YmlpYWluc3VyYW5j/ZS5jb20vd3AtY29u/dGVudC91cGxvYWRz/LzIwMTUvMDUvbm8t/aW1hZ2UuanBn"
                      />
                    </CardActionArea>
                 </Card>
              </Grid>
           ))}
         </Grid>
    
         <Modal
            open={selectedItem !== null} onClose={handleClose}
            aria-labelledby="modal-modal-title"
            aria-describedby="modal-modal-content"
         >
            <Box sx={modalStyle}>
               <Stack
                  sx={{ pb: 2}} direction="row" spacing={20}
               >
                  <Stack
                     sx={{ pb: 2 }} direction="column" spacing={6}
                     justifyContent="center" marginRight={10} paddingLeft={3}
                  >
                     <Typography id="modal-muthor" variant="h6" component="h2" paddingBottom={2}>
                        Post Author: <em>{selectedItem.author.displayName}</em>
                     </Typography>
                     <Typography id="modal-title" variant="h4" component="h2">
                        <b>{selectedItem.Title}</b>
                     </Typography>
                     <Typography id="modal-modal-content" sx={{ mt: 2 }}>
                       {selectedItem.Content}
                     </Typography>
                  </Stack>
                  <Box component="img" sx={{height: 300, width: 300}}
                       alt="Post Picture"
                       src="https://imgs.search.brave.com/QN5ZdDJqJOAbe6gdG8zLNq8JswG2gpccOqUKb12nVPg/rs:fit:260:260:1/g:ce/aHR0cHM6Ly93d3cu/YmlpYWluc3VyYW5j/ZS5jb20vd3AtY29u/dGVudC91cGxvYWRz/LzIwMTUvMDUvbm8t/aW1hZ2UuanBn"
                  />
                </Stack>
             </Box>
          </Modal>
      </Container>
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search