У меня есть компонент с другим дочерним компонентом, и я передаю переменную состояния JSON родительского компонента в качестве опоры для дочернего компонента, но когда я изменяю JSON в дочернем компоненте, переменная состояния родительского компонента также изменяется. В этом нет смысла, потому что это происходит просто с реквизитами JSON, но если я использую строки, числа или массивы, он работает хорошо, а переменные дочернего состояния просто изменяются. Это мои компоненты:

class Child extends React.Component{
    constructor(props){
        super(props)
        this.state={
            test2: this.props.data,
            
        }
        this.changeTextField = this.changeTextField.bind(this)
    }
    changeTextField(e){
        let data = this.state.test2
        data['name'] = e.target.value
        this.setState({test2: data})
    }
    render(){           
        return(
            <div> 
                <input type="text" value={this.state.test2['name']} onChange={this.changeTextField}/>                                  
            </div>  
        )
    }
}

class Parent extends React.Component{
    constructor(props){
        super(props)
        this.state={
            test: {name: "hola"},
            editing: false,
        }
        this.edit = this.edit.bind(this)
        this.cancel = this.cancel.bind(this)
    }
    edit(){
        this.setState({editing: true})
    }
    cancel(){
        this.setState({editing: false})
    }
    render(){        
        return(
            <div>
                {(this.state.editing) ?
                     <React.Fragment>
                        <Child data={this.state.test}/>   
                        <button onClick={this.cancel}>cancelar</button>
                    </React.Fragment>   
                :
                    <React.Fragment>
                        <h1>{this.state.test['name']}</h1>
                        <button onClick={this.edit}>edit</button>   
                    </React.Fragment>
                }
            </div>
        )
    }
}

$(document).ready(function(){
    ReactDOM.render(<Parent/>, document.getElementById("app"))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> 
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>  
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    
   
</head>
<body>
    <div id="app"></div>
    <script src="parent.jsx" type="text/babel"></script>
</body>
</html>
0
AlexisMunoz 14 Апр 2020 в 23:15

1 ответ

Лучший ответ

Вот как JavaScript работает с объектами. Они всегда передаются по ссылке, а остальные (строки, логические значения, числа, как вы упомянули) - это примитивы, означающие, что они неизменяемы.

На SO уже есть много удивительных ответов по этому поводу:

  1. JavaScript по ссылке против по значению
  2. Является ли JavaScript языком передачи по ссылке или передачей по значению?

Как это решить?

В фрагменте кода, где вы говорите data['name'] = e.target.value, вы все еще изменяете объект состояния, что, безусловно, является запретом в React. Вы можете прочитать Возможность не изменять контент в React Docs.

Вы можете создать копию test2 и вместо этого изменить ее:

const data = {...this.state.test2};
data['name'] = e.target.value

Но есть вероятность, что эта функция будет вызвана программно, это приведет к ошибке, потому что setState асинхронно. Вместо этого он дает нам функциональную версию, с которой можно справиться:

this.setState(prevState => ({
  test2: {
    ...prevState.test2,
    name: value,
  }
}));

Полная демонстрация:

class Child extends React.Component{
    constructor(props){
        super(props)
        this.state={
            test2: this.props.data,
            
        }
        this.changeTextField = this.changeTextField.bind(this)
    }
    changeTextField(e){
        const value = e.target.value
        this.setState(prevState => ({
          test2: {
            ...prevState.test2,
            name: value,
          }
        }))
    }
    render(){           
        return(
            <div> 
                <input type="text" value={this.state.test2['name']} onChange={this.changeTextField}/>                                  
            </div>  
        )
    }
}

class Parent extends React.Component{
    constructor(props){
        super(props)
        this.state={
            test: {name: "hola"},
            editing: false,
        }
        this.edit = this.edit.bind(this)
        this.cancel = this.cancel.bind(this)
    }
    edit(){
        this.setState({editing: true})
    }
    cancel(){
        this.setState({editing: false})
    }
    render(){        
        return(
            <div>
                {(this.state.editing) ?
                     <React.Fragment>
                        <Child data={this.state.test}/>   
                        <button onClick={this.cancel}>cancelar</button>
                    </React.Fragment>   
                :
                    <React.Fragment>
                        <h1>{this.state.test['name']}</h1>
                        <button onClick={this.edit}>edit</button>   
                    </React.Fragment>
                }
            </div>
        )
    }
}

$(document).ready(function(){
    ReactDOM.render(<Parent/>, document.getElementById("app"))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> 
    <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>  
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
    
   
</head>
<body>
    <div id="app"></div>
    <script src="parent.jsx" type="text/babel"></script>
</body>
</html>
2
Agney 14 Апр 2020 в 20:32