如何使用 ReactJs 中的键从父组件打开子组件模态

How to open child component modal from parent by using its key in ReactJs

我正在创建我的博客,我想通过单击它的编辑按钮分别编辑每个博客。 现在模式正在为各自的 id 打开,但未使用 state 值设置值。 那就是 componentwillmount 没有正常工作。

有什么建议我在这里犯了什么错误吗?这会很有帮助。 我怎样才能使用当前的代码集实现这一点。

//blog.js(父组件)

import React, {Component} from 'react';
import ReactGA from 'react-ga';
import {Card, Grid, Cell, Dialog, CardMenu, Button, CardTitle, CardText, CardActions, FABButton, Icon} from'react-mdl';
import { Container, Modal,  ModalHeader, ModalBody, Form, FormGroup, Label, Input,} from 'reactstrap';
import { connect } from 'react-redux';
import { getBlog, deleteBlog, updateBlog } from '../../actions/resumeActions';
import PropTypes from 'prop-types';
import Loading from './Loading';
import Moment from 'moment';
import BlogModal from "./BlogModal";
import Pagination from "react-js-pagination";
// import EditBlog from "./EditBlog";

class Blogs extends Component{
    initializeReactGA() {
        ReactGA.initialize('UA-132348738-1');
        ReactGA.pageview('/contact');
    }
    constructor(props) {
        super(props);
            this.state = {
                modal: false,
                justClicked: null,
                activePage: 1,
                requiredItem : null,
                _id: '',
                blog_short_desc: '',
                blog_name: '',
                blog_desc: '',
                blog_image_link: '',
                blog_by: '',
                blog_by_author: ''
            };
            this.handleOpenDialog = this.handleOpenDialog.bind(this);
              this.handleCloseDialog = this.handleCloseDialog.bind(this);
              this.replaceModalItem = this.replaceModalItem.bind(this);
            this.onTodoChange = this.onTodoChange.bind(this);
        }

    static propTypes = {
        getBlog: PropTypes.func.isRequired,
        deleteBlog: PropTypes.func.isRequired,
        updateBlog: PropTypes.func.isRequired,
        resume: PropTypes.object.isRequired,
        auth: PropTypes.object.isRequired,
        loading: PropTypes.object.isRequired
    }

    UNSAFE_componentWillReceiveProps(nextProps) {
        this.setState({
            value: nextProps.name
        })
    }

    toggle = (id) => {
        this.setState({
            modal: !this.state.modal
        });
    }

    componentWillMount() {
debugger
        this.props.resume["blogs"].map((blog) => {
            this.setState({
                updatable : false,
                _id: blog._id,
                blog_short_desc: blog.blog_short_desc,
                blog_name: blog.blog_name,
                blog_desc: blog.blog_desc,
                blog_image_link: blog.blog_image_link,
                blog_by: blog.blog_by,
                blog_by_author: blog.blog_by_author
            });
        })
    }

    replaceModalItem(id) {
        debugger
        this.setState({
          modal: true,
          requiredItem: id

        });
        debugger
    }

    onTodoChange = (e) => {
        this.setState({ 
            [e.target.name] : e.target.value 
        });
    }

    onSubmit = (e, id) => {
        e.preventDefault();
        const updatedBlog = {
            blog_short_desc: this.state.blog_short_desc,
            blog_name: this.state.blog_name,
            blog_desc: this.state.blog_desc,
            blog_image_link: this.state.blog_image_link,
            blog_by:  this.props.auth["user"]._id,
            blog_by_author: this.props.auth["user"].name
        }
        //update blog via updateblog action
        this.props.updateBlog(id, updatedBlog, this.props.history);
        alert("Blog updated successfully!");
        //close modal
        e.target.reset();
        this.toggle();
    }

    handleOpenDialog(id) {
        this.setState({
          openDialog: true,
          OpenEditDialog: true,
          justClicked: id

        });
    }

    handleCloseDialog() {
    this.setState({
        openDialog: false
    });
    }

    componentDidMount() {
        this.props.getBlog();
    }

    onDeleteBlogClick = (id) => {
        this.props.deleteBlog(id);
    };

    handlePageChange(pageNumber) {
        this.setState({activePage: pageNumber});
    }

    cardDialog(blogs, user){
        const itemsPerPage = 6; 
        let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
        return( 
            <Grid style={{padding: 0, display: 'contents'}}>

                {activeBlogs.map(({ _id, blog_name, blog_desc, blog_image_link, blog_by_author }) => (
                <Cell col={12}>
                    <Dialog open={this.state.openDialog && this.state.justClicked === _id} className="open-dialog">

                        {blog_image_link ?
                            (<CardTitle style={{color: '#fff', height: '176px', backgroundImage: `url(${blog_image_link})`, backgroundPosition: 'center',
                            backgroundSize: 'cover',
                            backgroundRepeat: 'no-repeat'}}>{blog_name}</CardTitle>) :

                            (<CardTitle className="card-blog-title-image">{blog_name}</CardTitle>
                            )
                        }
                        <CardText>
                            {blog_desc}
                        </CardText>
                        <CardActions border>
                        <p style={{float:'right', fontWeight:'bold'}}>Author: {blog_by_author}</p>
                        </CardActions>
                        <CardMenu style={{color: '#fff'}}>
                            <FABButton onClick={this.handleCloseDialog} className="close-button" >
                                <Icon name="close" />
                            </FABButton>
                        </CardMenu>
                    </Dialog>
                </Cell>
                ))}
            </Grid>
        )
    }

    editcardDialog(blogs, user){
        const itemsPerPage = 6; 
        let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
        return( 
            <span>
                <a className="btn edit-btn-blog-post" href="#" onClick={this.toggle}  title="Edit Blog">
                    <i className="fa fa-pencil" aria-hidden="true"></i>
                </a>
                {activeBlogs.map(({ _id, blog_short_desc, blog_name, blog_desc, blog_image_link, blog_by_author }) => (
                <Modal 
                    isOpen = {this.state.modal && this.state.requiredItem === _id}
                    toggle = {()=>this.toggle(_id)}    
                >
                    <ModalHeader toggle={this.toggle}  style={{fontWeight: "bold"}}>
                        Edit your blog {this.state.blog_name}
                    </ModalHeader>
                    <ModalBody>
                        <Form onSubmit={e => this.onSubmit(e, this.state._id )}>
                            <FormGroup>
                                <Label for="blogHeading">Blog Heading</Label>
                                <Input type="text" name="blog_short_desc" id="blogHeading" placeholder="Update one liner"
                                onChange={this.onTodoChange} value={blog_short_desc}/>
                                 <Label for="blogName">Blog Name</Label>
                                <Input type="text" name="blog_name" id="blogName" placeholder="Update blog name"
                                onChange={this.onTodoChange} value={blog_name}/>
                                <Label for="desc1">Description </Label>
                                <Input type="textarea" name="blog_desc" id="desc1" placeholder="Update your blog"
                                onChange={this.onTodoChange} value={blog_desc}/>
                                 <Label for="imageUrl">Image Url</Label>
                                <Input type="text" name="blog_image_link" id="imageUrl" placeholder="Update image url (Optional)"
                                onChange={this.onTodoChange} value={blog_image_link}/>
                                <Button
                                    color="dark"
                                    style={{marginTop: '2rem'}}
                                    block
                                >Edit blog</Button>
                            </FormGroup>
                        </Form>
                    </ModalBody>
                </Modal>
                ))}
            </span>
        )
    }

    render(){
        const { blogs, loading} = this.props.resume;
        const {  user, isAuthenticated } = this.props.auth;
        const itemsPerPage = 6; 
        let activeBlogs = blogs.slice (itemsPerPage * this.state.activePage - itemsPerPage, itemsPerPage * this.state.activePage);
        return(
            <Container>

            {loading ? (
            <div><Loading/></div>
            ) : (
                <div>
                    {/* blog modal */}
                    <BlogModal />

                    {/* card dialog */}
                    {this.cardDialog(blogs, user)}

                    {this.editcardDialog(blogs, user)}

                    <Grid style={{padding: 0}} id="todo">
                        {activeBlogs.map((item, i) => (
                            <Cell key={item._id} data-id={item._id}>   
                                <Card shadow={5} className="cards-grid">


                                    {item.blog_image_link ?
                                        (<CardTitle style={{color: '#fff', height: '200px',
                                        width: 'auto', backgroundImage: `url(${item.blog_image_link})`, backgroundPosition: 'center',
                                        backgroundSize: 'cover',
                                        backgroundRepeat: 'no-repeat'}}></CardTitle>) :

                                        (<CardTitle className="card-title-image"></CardTitle>
                                        )
                                    }

                                    <CardText>
                                        <b>{item.blog_short_desc}</b>
                                    </CardText>

                                    <CardActions border>
                                        <p>
                                        <Button className="blog-read-me-button col-4" onClick={this.handleOpenDialog.bind(this, item._id)}>Read </Button> 

                                        { isAuthenticated && (item.blog_by === user._id) ? 
                                        <span className="col=8">

                                        <Button className="remove-btn-blog-post"
                                        color="danger"
                                        size="sm"
                                        onClick= {this.onDeleteBlogClick.bind(this, item._id)} title="Delete Blog">
                                            &times;
                                        </Button> 
                                        <a className="btn edit-btn-blog-post" href="#" onClick={this.replaceModalItem.bind(this, item._id)}  title="Edit Blog">
                                            <i className="fa fa-pencil" aria-hidden="true"></i>
                                        </a> 

                                        {/* <a className="btn edit-btn-blog-post" href="#" onClick={this.handleEditOpenDialog.bind(this, item._id)}  title="Edit Blog">
                                            <i className="fa fa-pencil" aria-hidden="true"></i>
                                        </a> */}

                                        </span> : null }
                                        </p>

                                        <p style={{ fontStyle:'italic', fontWeight:'bold'}}>By-{item.blog_by_author} <span style={{float:'right',}}>{Moment(item.date).format('Do MMMM YYYY')}</span></p> 
                                    </CardActions>
                                </Card>  
                            </Cell>  
                        ))} 
                    </Grid>
                </div> 
                )}
                <Pagination
                    activePage={this.state.activePage}
                    itemsCountPerPage={6}
                    totalItemsCount={blogs.length}
                    pageRangeDisplayed={5}
                    onChange={this.handlePageChange.bind(this)}
                    itemClass='page-item'
                    linkClass='page-link'
                />
            </Container>
        ) 
   }
}

const mapStateToProps = (state) => ({
    resume: state.resume,
    auth: state.auth,
    loading: state.apiCallsInProgress > 0
});

export default connect(mapStateToProps, {getBlog, deleteBlog, updateBlog }) (Blogs);

试试下面的

 class EditBlog extends Component {
        constructor(props) {
            super(props);
                this.state = {
                    modal: null,
                    requiredItem : null,
                    _id: '',
                    blog_short_desc: '',
                    blog_name: '',
                    blog_desc: '',
                    blog_image_link: '',
                    blog_by: '',
                    blog_by_author: ''
                };
                this.replaceModalItem = this.replaceModalItem.bind(this);
                this.onTodoChange = this.onTodoChange.bind(this);

            }

        static propTypes = {
            auth: PropTypes.object.isRequired,
            updateBlog: PropTypes.func.isRequired,
            editBlog: PropTypes.func.isRequired,
            resume: PropTypes.object.isRequired,
        }

        UNSAFE_componentWillReceiveProps(nextProps) {
            this.setState({
                value: nextProps.name
            })
        }
        toggle = (id) => {
            this.setState({
                modal: id
            });
        }
        componentWillMount() {

            this.props.resume["blogs"].map((blog) => {
                this.setState({
                    updatable : false,
                    _id: blog._id,
                    blog_short_desc: blog.blog_short_desc,
                    blog_name: blog.blog_name,
                    blog_desc: blog.blog_desc,
                    blog_image_link: blog.blog_image_link,
                    blog_by: blog.blog_by,
                    blog_by_author: blog.blog_by_author
                });
            })
        }

        replaceModalItem(id) {
            this.setState({
              openDialog: true,
              OpenEditDialog: true,
              requiredItem: id

            });
        }

        onTodoChange = (e) => {
            this.setState({ 
                [e.target.name] : e.target.value 
            });
        }

        onSubmit = (e, id) => {
            e.preventDefault();
            const updatedBlog = {
                blog_short_desc: this.state.blog_short_desc,
                blog_name: this.state.blog_name,
                blog_desc: this.state.blog_desc,
                blog_image_link: this.state.blog_image_link,
                blog_by:  this.props.auth["user"]._id,
                blog_by_author: this.props.auth["user"].name
            }
            //update blog via updateblog action
            this.props.updateBlog(id, updatedBlog, this.props.history);
            alert("Blog updated successfully!");
            //close modal
            e.target.reset();
            this.toggle();
        }
        render(){

            return(
                <span>
       <a className="btn edit-btn-blog-post" href="#" onClick={()=>this.toggle(this.state._id)} title="Edit Blog">
                    <i className="fa fa-pencil" aria-hidden="true"></i>
                </a>
                    <Modal 
                        isOpen = {this.state.modal===this.state._id}
                        toggle = {()=>this.toggle(this.state._id)}   
                    >
                        <ModalHeader toggle={()=>this.toggle(this.state._id)} style={{fontWeight: "bold"}}>
                            Edit your blog {this.state.blog_name}
                        </ModalHeader>
                        <ModalBody>
                            <Form onSubmit={e => this.onSubmit(e, this.state._id, )}>
                                <FormGroup>
                                    <Label for="blogHeading">Blog Heading</Label>
                                    <Input type="text" name="blog_short_desc" id="blogHeading" placeholder="Update one liner"
                                    onChange={this.onTodoChange} value={this.state.blog_short_desc}/>
                                     <Label for="blogName">Blog Name</Label>
                                    <Input type="text" name="blog_name" id="blogName" placeholder="Update blog name"
                                    onChange={this.onTodoChange} value={this.state.blog_name}/>
                                    <Label for="desc1">Description </Label>
                                    <Input type="textarea" name="blog_desc" id="desc1" placeholder="Update your blog"
                                    onChange={this.onTodoChange} value={this.state.blog_desc}/>
                                     <Label for="imageUrl">Image Url</Label>
                                    <Input type="text" name="blog_image_link" id="imageUrl" placeholder="Update image url (Optional)"
                                    onChange={this.onTodoChange} value={this.state.blog_image_link}/>
                                    <Button
                                        color="dark"
                                        style={{marginTop: '2rem'}}
                                        block
                                    >Edit blog</Button>
                                </FormGroup>
                            </Form>
                        </ModalBody>
                    </Modal>
                </span>
            )
        }
    }

    const mapStateToProps = state => ({
        resume: state.resume,
        auth: state.auth
    })

    export default connect(mapStateToProps, { updateBlog })(EditBlog);

为什么不使用父组件处理所有帖子,而将 <EditBlog / 仅用作功能组件。这是一个例子和相应的 fiddle:

const EditBlog = ({title, content, handleEdit}) => {
return (
                <div classname="editblog">
          <h1>{title}</h1>
          <p>{content}</p>
          <button onClick={handleEdit}>Edit</button>
        </div>
        )
}

class Blog extends React.Component {
constructor(props) {
super(props);
this.state= {
activeBlogs: [
{title: "Heading 1", content: "Content 1"},
{title: "Heading 2", content: "Content 2"}
],
editId: -1,
}
this.handleEdit = this.handleEdit.bind(this);
}

handleEdit(e) {
const {editId, activeBlogs} = this.state;
let newActiveBlogs = [...activeBlogs];
const {name, value } = e.target;
newActiveBlogs[editId]= Object.assign(newActiveBlogs[editId], {[name]: value});
if(editId >= 0) {
this.setState({
activeBlogs: newActiveBlogs
})
}

}
  render() {
  const {activeBlogs, editId} = this.state;
    return (
    <div classname="blog">
    {activeBlogs.map((item, index) => {
        return <EditBlog key={index} {...item} handleEdit={()=>this.setState({editId: index})} />
    })} 
    {editId >= 0 && activeBlogs.length && 
    <div classname="modal">
        <h2>Edit Modal is open</h2>
        <input name="title" value={activeBlogs[editId].title} onChange={this.handleEdit}></input>
        <input name="content" value={activeBlogs[editId].content} onChange={this.handleEdit}></input>
        <button onClick={()=>this.setState({editId:-1})}>Save</button>
    </div>
    }
    </div>
  )
}
}

ReactDOM.render(
  <Blog name="Blog" />,
  document.getElementById('container')
);

fiddle