5

I am making a social media app. I am looping through all the comments and showing it on UI. When I click on edit, always the last comment's text show up on input. I tried many different things, changed structure, used bind to bind the context, but nothing has helped.

I am using React Material UI.

Here is the code:

render() {

    const { anchorEl } = this.state;
    const open = Boolean(anchorEl);

    return(
        <Panel>
            <form noValidate autoComplete="off" onSubmit={this.onSubmit}>
                <TextField
                    id={`comment${this.state.post_id}`}
                    name="comment"
                    fullWidth
                    placeholder="Comment here"
                    margin="normal"
                    value={this.state.comment}
                    onChange={this.handleChange}
                    InputProps={{
                        endAdornment : <InputAdornment position="end">
                            <IconButton onClick={this.resetTextField}>
                                <CloseIcon/>
                            </IconButton>
                        </InputAdornment>
                    }}
                />
            </form>

            {this.props.comments && this.props.comments.length > 0 &&
            <RenderComments
                comments={this.state.comments}
                open={open}
                anchorEl={this.state.anchorEl}
                handleClick={this.handleClick}
                handleClose={this.handleClose}
                handleDelete={this.handleDelete}
                handleEdit={this.handleEdit}
            />
            }
        </Panel>
    )
}

const RenderComments = (props) => {
    return props.comments.map(comment =>
        <CommentBase
            key={comment.id}
            comment={comment}
            open={props.open}
            anchorEl={props.anchorEl}
            handleClick={props.handleClick}
            handleClose={props.handleClose}
            handleDelete={props.handleDelete}
            handleEdit={props.handleEdit}
        />
    );
};

    const CommentBase = ({comment, open, anchorEl, handleClick, handleClose, handleDelete, handleEdit}) => (
    <CommentBasePanel>
        <CommentText>
            {comment.text}
            <span style={{ float: 'right', fontSize: 10, color: '#A9A9A9' }}>{moment(comment.created_at).fromNow()}</span>
        </CommentText>
        <HelperAction>
            <MoreVertIcon
                id={comment.id}
                aria-owns={open ? `simple-popper${comment.id}` : null}
                aria-haspopup="true"
                variant="contained"
                onClick={handleClick}
            />
            <Popover
                id={`simple-popper${comment.id}`}
                open={open}
                anchorEl={anchorEl}
                onClose={handleClose}
                anchorOrigin={{
                    vertical: 'bottom',
                    horizontal: 'right',
                }}
                transformOrigin={{
                    vertical: 'top',
                    horizontal: 'right',
                }}
            >
                <Typography style={{ padding: 10, cursor: 'pointer' }} onClick={() => handleEdit(comment)}>
                    Edit
                </Typography>
                <Typography style={{ padding: 10, color: red[500], cursor: 'pointer' }} onClick={() => handleDelete(comment.id)}>
                    Delete
                </Typography>
            </Popover>
        </HelperAction>
    </CommentBasePanel>
);

handleEdit = (comment) => {
    this.setState({ comment: comment.text, comment_id: comment.id })
};

A console log on comment here in handleEdit method always logs the last comment no matter what comment I edit. Edit on first comment gives last comment text as you can see in the image.

enter image description here

Parth Chokshi
  • 642
  • 4
  • 16
  • It seems the issue is with Material UI popover, as that is not visible while loading and that is causing some ref issue. Getting Edit and Delete text directly on screen out of popover solved the issue. – Parth Chokshi Sep 10 '18 at 23:04
  • check this - render `{comment.id}` in ``? it's rather scope related – xadm Sep 10 '18 at 23:31

2 Answers2

6

Bad Popovers management

map copies the same open and anchorEl props to all Popover instances - handleClick (you didn't show this) *opens all of them in the same place** (the last one is on top).

FIX: include id in handleClick, save in state and use in condition for open property

FIX2: You can use one <Popover/> instance especially when not displaying any content related to specific comment.

PS. I spent more time recreating this (guessing missing parts) in stackblitz than real debugging (in fact only checking html structure for <Popover/> with rendered {comment.id}). Next time show more complete code or provide minimal working example.

xadm
  • 8,219
  • 3
  • 14
  • 25
0

If you update your RenderComments render method as follows, it should resolve your problem:

const RenderComments = (props) => {
    return props.comments.map(comment =>
        <CommentBase
            key={comment.id}
            comment={comment}
            open={props.open}
            anchorEl={props.anchorEl}
            handleClick={ props.handleClick }
            handleClose={ props.handleClose }
            handleDelete={ () => props.handleDelete(comment.id) }
            handleEdit={ () => props.handleEdit(comment) }
        />
    );
};
Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
  • Thanks but it doesn't work. Can't see the float of edit anymore. I think that's cz handleClick is prop too and not a synthetic event like onClick. – Parth Chokshi Sep 10 '18 at 22:42
  • @ParthChokshi just updated - does this work for you? – Dacre Denny Sep 10 '18 at 22:48
  • Nope. It doesn't. That handleEdit is called from inside the functional component of CommentBase. Though it doesn't make any difference here, but it should be handleEdit = {cmnt => props.handleEdit(cmnt)}; – Parth Chokshi Sep 10 '18 at 22:52