【问题标题】:React Redux - React side filtering unmount child component on editing when the child component filter field is no more matchingReact Redux - 当子组件过滤器字段不再匹配时,React 侧过滤在编辑时卸载子组件
【发布时间】:2020-11-04 21:03:24
【问题描述】:

我正在尝试在我的应用程序中实现过滤功能并在反应端实现过滤。有一个父组件(AllAdmins)连接到 redux 并获取所有用户并在页面上显示为用户列表。此页面有一个下拉过滤器,用于按状态过滤用户。列表中的每个用户都是一个子组件(AdminDetails),它有编辑按钮来编辑这个特定的用户。单击“编辑”按钮时,将显示模态(EditAdminModal),我们可以在其中编辑用户的状态。在编辑状态时,我正在更新 onChange 事件的商店。一旦商店更新,如果过滤器不再匹配这个更新的值,这个正在编辑的子组件(AdminDetails)被卸载并且更新模式(EditAdminModal)和子组件(AdminDetails)消失。

这是基于实现的正确行为,我理解问题是因为我正在根据道具进行过滤,现在这个孩子的道具已在 redux 商店中更新。我想知道这是否不是实现此功能的正确方法,还有其他方法可以实现。

如果有人可以提供帮助,我将不胜感激,我一直被困在这个问题上,并且我正在避免服务器端过滤。

AllAdmins 父组件

class AllAdmins extends Component{
    constructor(props){
        super(props);
        this.state = {
            filter: ""
        };
    }

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

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

    render() {
        let filteredAdmins = this.props.admins.filter(admin => admin.status.indexOf(this.state.filter)!==-1);
        
        return(
        <div className="top-align">
            {redirectVar}
            <div className="heading py-1">
                <h4 className="font-weight-bold">&nbsp;&nbsp;<i className="fas fa-user"></i> Admin Users</h4>
            </div>
            
            <div className="container-fluid events-below-heading">
                <div className="events-search-section">
                    <h4 className="text-center text-white all-events-heading p-1 mt-2">All Admin Users</h4>
                    <div className="row">
                        <div  class="col-6 col-sm-2 order-sm-3">
                            <select className="form-control" name="filter" onChange={this.handleChange}
                             value={this.state.filter}>
                                <option selected value="">Filter by Status</option>
                                <option>Active</option>
                                <option>Inactive</option>
                            </select>
                        </div>
                    </div>
                    <hr/>
                </div>
                <h6 style= {{color:"red"}}>{this.props.responseMessage}</h6>
                {
                    filteredAdmins.length!==0 ? filteredAdmins.map(admin=>
                    <AdminDetails admin={admin} key={admin._id}/>
                    )
                    :
                    <h2>null</h2>
                    
                }
            </div>
        </div>)
    }
}
        
const mapDispatchToProps = dispatch => {
    return {
        getAdmins: () => {dispatch(getAdmins())}
    }
}

const mapStateToProps = state => {
    return {
        admins: state.adminUsers.admins
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(AllAdmins);

AdminDetails 子组件

class AdminDetails extends Component{
    constructor(props){
        super(props);
        this.state = {
            showEditAdminModal: false
        };
    }

    showEditAdminModal = e => {
        this.setState({showEditAdminModal: true});
    }
    
    hideEditAdminModal = e => {
        this.setState({showEditAdminModal: false});
    }
    
    render() {
        return(
            <div className="row justify-content-center mt-3">
                <div className="col-sm-8">
                    <div className="card d-flex flex-row">
                        <div className="card-body card-body-lesspad">
                            <h5 className="card-title font-weight-bold">{`${this.props.admin.fname} ${this.props.admin.lname}`}</h5>
                            <p className="card-text font-smaller"><strong>SJSU ID: </strong>{this.props.admin.id}</p>
                            <p className="card-text font-smaller"><strong>Email ID: </strong>{this.props.admin.email}</p>
                            <p className="card-text font-smaller"><strong>Status: </strong>{this.props.admin.status}</p>
                            <p className="card-text font-smaller"><strong>Created Date: </strong>
                                {new Date(this.props.admin.createdDate).toLocaleString()}
                            </p>
                            <p className="card-text font-smaller"><strong>Updated Date: </strong>
                                {new Date(this.props.admin.updatedDate).toLocaleString()}
                            </p>
                            <button type="button" className="btn btn-link view-details-color"
                            onClick = {this.showEditAdminModal}>
                                <i className="fas fa-edit"/> Edit
                            </button>
                        </div>
                    </div>
                </div>
                {this.state.showEditAdminModal ? 
                <EditAdminModal hideEditAdminModal={this.hideEditAdminModal}
                admin={this.props.admin}/> : null}
            </div>
        )
    }
}

export default AdminDetails;

EditAdminModal 组件

class EditAdminModal extends Component{
    handleChange = e => {
        const { name, value } = e.target;
        this.props.handleChange(this.props.admin._id, name, value);
    }

    handleUpdate = e => {
        e.preventDefault();

        this.setState(
                { 
                    message: "",
                    loader: true
                }
            );
        this.props.updateAdmin(this.props.admin)
        .then(() => {
            this.props.hideEditAdminModal();
        })
        .catch(() => {
            this.setState({
                message: "Some Internal Error occured. Please refresh and check if the admin is updated. If not, please try again after sometime. If the problem persist, please comtact Admin."
            });
        });
    }

    options = ['Active', 'Inactive'];
    
    render() {
        return(
        <div>
            <div className="modal">
                <div className="modal-dialog modal-dialog-centered modal-dialog-scrollable">
                    <div className="modal-content">
                        <div className="modal-header">
                            <h5 className="modal-title" id="itemModal">Add Admin</h5>
                        </div>
                        <div className="modal-body">
                            <h6 style= {{color:"red"}}>{this.state.message}</h6>
                            <h6 style= {{color:"red"}}>{this.props.responseMessage}</h6>
                            
                            <div class="form-group row">
                                <label className="col-4">Status</label>
                                <div className="col-8">
                                    <select className="form-control" name="status" onChange={this.handleChange}>
                                        {
                                            this.options.map( option => {
                                                if(option === this.props.admin.status){
                                                    return <option selected key={option}>{option}</option> ;
                                                } else {
                                                    return <option key={option}>{option}</option> ;
                                                }
                                            }
                                            )}
                                    </select>
                                </div>
                            </div>
                        </div>
                        <div className="modal-footer">
                            <button type="button" onClick = {this.handleEditCancel} className="btn btn-primary btn-style" 
                                data-dismiss="modal">Cancel</button>
                            <button onClick = {this.handleUpdate}
                                className="btn btn-primary btn-style">Update</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
    }
}

const mapDispatchToProps = dispatch => {
    return {
        handleChange: (id, name, value) => {dispatch(adminChangeHandler(id, name, value))},
        updateAdmin: admin => dispatch(updateAdmin(admin))
    };
};

const mapStateToProps = state => {
    return {
        responseMessage: state.adminUsers.createResponseMessage
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(EditAdminModal);

【问题讨论】:

    标签: javascript reactjs redux


    【解决方案1】:

    我马上想到的是将您的值(状态)存储在本地组件状态中,使用它,并且只有当用户单击保存按钮时,您才会将其推送到 redux 存储。

    所以它的某些部分看起来像:

    class EditAdminModal extends Component{
          constructor(props) {
              super(props);
              this.state = {
                  status: props.admin.status;
              }
          }
          //... some other code
          onChange => (e) {
              const {value} = e;
              this.setState({status:value};
          }
          //... some other code
    }
    

    然后在你的选择中你会这样做:

                                        <select className="form-control" name="status" onChange={this.onChange}> //here you use new onChange
                                        {
                                            this.options.map( option => {
                                                if(option === this.state.status){ //here you use local state
                                                    return <option selected key={option}>{option}</option> ;
                                                } else {
                                                    return <option key={option}>{option}</option> ;
                                                }
                                            }
                                            )}
                                    </select>
    

    然后在您的handleUpdate 中执行以下操作:

    this.props.updateAdmin({...this.props.admin, status:this.state.status})
    

    而不仅仅是this.props.updateAdming(this.props.admin)

    【讨论】:

    • 感谢@Nikita 的回复。我对这个解决方案有一个想法,但这不会是 Facebook 的反模式吗?根据 FB ** 在 getInitialState 中使用 props 生成状态通常会导致重复“真实数据源”,即真实数据所在的位置。这是因为 getInitialState 仅在首次创建组件时调用。 **如果我错了,请纠正我。谢谢
    • 这不是反模式,但确实可以讨论 - redux.js.org/faq/organizing-state 这取决于您对编辑组件的看法 - 您会看到它是一个模态窗口,类似于“我在我得救之前,我不是真理的来源”。想一想,通常在模态中,除非您实际按下“保存”,否则不会提交任何内容。所以从这个角度来看没有冲突 - 模态在本地维护其可编辑的内容,然后提交到单一的事实来源 - 在这种情况下是 redux
    • 如果你真的想在 redux 存储中拥有它,你的另一个选择是为模态创建另一个减速器,并将你的值复制到那里,在那里编辑然后复制回来。在这种情况下,您将拥有一个“事实来源”——即 redux,但同时您的商店会变得更加混乱,我相信它更难理解
    • 哦,顺便说一下,在你读过这个“反模式”的同一个网站上,它被明确提到:但是,如果你清楚地表明它不是反模式只是组件内部控制状态的种子数据。 - zhenyong.github.io/react/tips/…
    • 非常感谢 Nikita,非常感谢您的所有回答和澄清。这是要接受的答案。我试过了,效果很好。
    猜你喜欢
    • 1970-01-01
    • 2021-05-22
    • 2018-04-08
    • 1970-01-01
    • 2021-03-28
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多