简体   繁体   中英

React Childs component changes his parents state

I am working on my first bigger react project and found this bug/feature which I don know how to solve.

Problem

I want to edit the user by modal. I opened it, but when I change something in the field, parents components state changes too. Child component is changing parent state without passed function.

My approach

I tried changing variables name because I think it is impossible to change parent components state. Then I started logging when the state is changed. On first call of this.props.log() in handle change, the state is changed.

Parent component

class UserAdminPage extends Component {
    constructor(props) {
        super(props);
        this.state = {
            performingAction: false,
            editorIsOpened: false,
            selectedUser: {}
        }
    }

    componentDidMount() {
        userService.getAll().then(res => {
            const users = res;
            this.setState({users});
        })
    }

    openEditModal = id => {
        const user = this.state.users.filter(u => u.id === id)[0];
        this.setState({selectedUser: user}, () => {
            this.setState({editorIsOpened: true})
            console.log("Selected user", this.state.selectedUser);
        });
    }
    closeModal = () => {
        console.log("Close values",this.state.users, this.state.selectedUser);

        /*if (!save) {
            console.log("Throwing away");
            this.setState({editorIsOpened: false});
            return;
        }
        console.log("Saving");
        this.setState({editorIsOpened: false});*/
    }
    log = ()=>{
        console.log("Logged user",this.state.users);
    }

    render() {
        const {users} = this.state;
        return (
            users ? (
                <React.Fragment>
                    <MDBRow className={"my-5"}/>
                    <UserTable users={users} openEditModal={this.openEditModal}
                               performingAction={this.state.performingAction}/>
                    {this.state.editorIsOpened && <UserEditorModal user={this.state.selectedUser}
                                                                   closeModal={this.closeModal} log={this.log}/>}
                </React.Fragment>
            ) : (
                <LaunchScreen/>
            )
        )
    }
}

And child

class UserEditorModal extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedUser: props.user
        }
        console.log("Modal user",this.state.selectedUser)
    }

    handleChange = (e) => {
        this.props.log();//here is different output
        const [field, value] = [e.target.name, e.target.value]
        const parsedValue = parseInt(value)
        const user = this.state.selectedUser;
        user.hasOwnProperty(field) ? user[field] = isNaN(parsedValue)?value:parsedValue : console.error(`${field} not found on ${user}`);
        this.setState({selectedUser:user});
        this.props.log();
    }

    render() {
        const user = this.state.selectedUser;
        const modal = true;
        return (
            <React.Fragment>
                <MDBModal isOpen={modal} toggle={() => this.props.closeModal(null, false)} fullHeight
                          position="right">
                    <MDBModalHeader toggle={() => this.props.closeModal()}>User editor</MDBModalHeader>
                    <MDBModalBody>
                        <MDBRow>
                            <MDBCol size="3">
                                <MDBInput type='number' name="karma" label="Karma" value={user.karma}
                                          onChange={e => this.handleChange(e)}/>
                            </MDBCol>
                            <MDBCol size="3">
                                <MDBInput type='number' name="money" label="Money" value={user.money}
                                          onChange={e => this.handleChange(e)}/>
                            </MDBCol>
                            <MDBCol>
                                <MDBInput type="text" value={user.email} disabled/>
                            </MDBCol>
                        </MDBRow>
                        <MDBRow>
                            <MDBCol size="md">
                                <MDBInput type='text' name="firstName" label="First name" value={user.firstName}
                                          onChange={e => this.handleChange(e)}/>
                            </MDBCol>
                            <MDBCol size="md">
                                <MDBInput type='text' name="lastName" label="Last name" value={user.lastName}
                                          onChange={e => this.handleChange(e)}/>
                            </MDBCol>
                        </MDBRow>
                    </MDBModalBody>
                    <MDBModalFooter>
                        <MDBBtn color="secondary"
                                onClick={() => this.props.closeModal()}>Close</MDBBtn>
                        <MDBBtn color="primary" onClick={() => this.props.closeModal()}>Save
                            changes</MDBBtn>
                    </MDBModalFooter>
                </MDBModal>
            </React.Fragment>
        );
    }
}```
I must missing something but couldnt find it. Hope you can find it and help me once again

Guess it's because you are using the reference to change the state. So ultimately when you change the state of the child component, the parent's state will also change but parent won't re-render.

constructor(props) {
        super(props);
        this.state = {
            selectedUser: {...props.user}
        }
        console.log("Modal user",this.state.selectedUser)
    }

Try using this in your child.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM