2017-10-11 116 views
2

問:問題作出反應 - 兒童狀態改變父母的狀態

如果每個組件都有自己的狀態,那麼這件事情是怎麼發生的, 何其可能是孩子改變父母的狀態?


這裏是我的應用程序的完整代碼:

import React, { Component } from 'react'; 
import { render } from 'react-dom'; 
import AutoComplete from 'material-ui/AutoComplete'; 
import Chip from 'material-ui/Chip'; 
import Hello from './Hello'; 
import './style.css'; 
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; 

class App extends Component { 
    constructor() { 
    super(); 
    const vehicles = [{value : 1 , label : 'Vehicle 1'},{value : 2 , label : 'Vehicle 2'},{value : 3 , label : 'Vehicle 3'},{value : 4 , label : 'Vehicle 4'},{value : 5 , label : 'Vehicle 5'},{value : 6 , label : 'Vehicle 6'},{value : 7 , label : 'Vehicle 7'},{value : 8 , label : 'Vehicle 8'}]; 
    this.state = { 
     vehicles, 
     name: 'React', 
     name1: 'React1', 
     name2: 'React2' 
    }; 
    } 

    render() { 
    const dataSourceConfig = { 
      text: 'label', 
      value: 'value', 
     }; 
    return (
     <div> 
     <MuiThemeProvider> 
      <div> 
       <AutoCompleteHlpr 
       dataSource={this.state.vehicles} 
       dataSourceConfig={dataSourceConfig} 
       floatingLabelText='Select Vehicles' 
       selectedOption={this.handleSelectedVehicle}/> 

       <AutoCompleteHlpr 
       dataSource={this.state.vehicles} 
       dataSourceConfig={dataSourceConfig} 
       floatingLabelText='Select Vehicles' 
       selectedOption={this.handleSelectedVehicle}/> 

       <AutoCompleteHlpr 
       dataSource={this.state.vehicles} 
       dataSourceConfig={dataSourceConfig} 
       floatingLabelText='Select Vehicles' 
       selectedOption={this.handleSelectedVehicle}/> 
      </div> 
     </MuiThemeProvider> 
     <Hello name={this.state.name} /> 
     <p> 
      Start editing to see some magic happen :) 
     </p> 
     </div> 
    ); 
    } 
} 




export class AutoCompleteHlpr extends React.Component { 

    constructor(props) { 
     super(props); 
     this.state = {dataSource : this.props.dataSource , searchText : ''}; 

     this.styles = { 
      chip: { 
       margin: 4, 
      }, 
      wrapper: { 
       display: 'flex', 
       flexWrap: 'wrap', 
      }, 
     }; 

     this.handleNewRequest = this.handleNewRequest.bind(this); 
     this.getDataSource = this.getDataSource.bind(this); 
     console.log(this.state); 
    } 

    handleNewRequest(searchText , index){ 
     this.state.dataSource[index]['selected'] = true; 
     this.setState({dataSource : this.state.dataSource , searchText : '' }); 
     this.props.selectedOption(this.state.dataSource[index] , this.state.dataSource); 
    } 

    renderChip(data , index) { 
     if(data.selected) { 
      var value = this.props.dataSourceConfig.text ? this.props.dataSourceConfig.text : 'text'; 
      var key = this.props.dataSourceConfig.value ? this.props.dataSourceConfig.value : 'value'; 
      return (
       <Chip 
        key={data[key]} 
        style={this.styles.chip} 
        onRequestDelete={() => this.handleRequestDelete(index)} > 
        {data[value]} 

       </Chip> 
      ); 
     } 
    } 

    handleRequestDelete(index) { 
     this.state.dataSource[index]['selected'] = false; 
     this.setState({dataSource : this.state.dataSource , searchText : '' }); 
     this.props.selectedOption(this.state.dataSource[index] , this.state.dataSource); 
    } 

    componentWillReceiveProps(nextProps) { 
     if(this.props.dataSource !== nextProps.dataSource) { 
      this.setState({ dataSource : nextProps.dataSource }); 
     } 
    } 

    getDataSource() { 
     return this.state.dataSource.map(data => { 
      if(!data.selected) { 
       return data 
      } 
     }) 
    } 

    render() { 
     return(
      <div> 
       <AutoComplete 
        floatingLabelText={this.props.floatingLabelText} 
        filter={AutoComplete.caseInsensitiveFilter} 
        onNewRequest={this.handleNewRequest} 
        searchText={this.state.searchText} 
        dataSource={this.getDataSource()} 
        dataSourceConfig={this.props.dataSourceConfig} 
        openOnFocus={true} 
       /> 
       <div style={this.styles.wrapper}> 
        {this.state.dataSource.map(this.renderChip, this)} 
       </div> 
      </div> 
     ) 
    } 
} 

AutoCompleteHlpr.defaultProps = { 
    floatingLabelText : 'Type Something', 
    dataSource : [], 
    dataSourceConfig : {}, 
    selectedOption:() => { } 
}; 

render(<App />, document.getElementById('root')); 

這裏是鏈接到stackblitz(在線工作代碼):

https://stackblitz.com/edit/react-svqwcg


如何產生的問題:

  1. 選擇 「車輛1」 從第三個文本框
  2. 第二個文本框
  3. 選擇 「車輛3」 第一個文本框
  4. 選擇 「汽車2」

你會明白的。

+0

請在問題本身中包含您的代碼。我不知道這個stackblitz網站是什麼,我不想訪問它,大多數訪問這個問題的人也不會。 –

+0

@JamesDonnelly,請訪問它的一種plunkr,在那個網站上我創建了整個演示的問題,在那裏你可以看到整個項目代碼。 –

+0

問題是,如果該網站出現故障或因其他原因無法訪問,則此問題變得毫無意義。 –

回答

1

你的問題是,在子組件中你正在改變父狀態。

this.state.dataSource[index]['selected'] = true; 

突變是在傳播到所有AutoCompleteHlpr部件,因爲你正在傳遞相同的數組到所有這些:當您更改項目的selected屬性在vehicles陣列它發生就行了。你造成的情況實際上你有一個全局狀態的所有AutoCompleteHlpr組件。

要修復您需要傳遞vehicles數組的克隆,那麼在克隆的對象上更改道具不會影響原始的道具。下面你有一個簡單的克隆實現,但你可以使用另一個lodash等。:

const clone = (arg) => JSON.parse(JSON.stringify(arg)); 

在你的代碼,你可以使用它像:

<AutoCompleteHlpr 
      dataSource={clone(this.state.vehicles)} 
      dataSourceConfig={dataSourceConfig} 
      floatingLabelText='Select Vehicles' 
      selectedOption={this.handleSelectedVehicle}/> 

另一種選擇是不是改變selected

this.state.dataSource[index]['selected'] = true; 

,你可以這樣做:

const newDataSource = this.state.dataSource.reduce((ds, item, idx) => index !== idx 
    ? ds.concat(item) 
    : ds.concat(Object.assign({}, item, { selected: true })), []); 
this.setState({ dataSource: newDataSource, searchText: '' }); 

編輯:看看以下的代碼片段,看看爲什麼路過[...this.state.vehicle]將不起作用:

const vehicles = [{value : 1 , label : 'Vehicle 1'},{value : 2 , label : 'Vehicle 2'},{value : 3 , label : 'Vehicle 3'},{value : 4 , label : 'Vehicle 4'},{value : 5 , label : 'Vehicle 5'},{value : 6 , label : 'Vehicle 6'},{value : 7 , label : 'Vehicle 7'},{value : 8 , label : 'Vehicle 8'}]; 
 

 
const vehicles2 = [...vehicles]; 
 
console.log(vehicles === vehicles2); 
 
console.log(vehicles[0] === vehicles2[0]); 
 
console.log(vehicles.every((item, idx) => item === vehicles2[idx]))

因此,大家可以看到,vehiclevehicle2是不同的陣列,但他們的項目是對同一個對象的引用!這就是您需要深度克隆的原因。

+0

好吧,它的工作原理,我也試過了,所以你只是在傳遞克隆的權利,所以我也可以在es6風格中做{...車輛},對吧? –

+0

我也將突變線替換爲var data_source = this.state.dataSource; data_source [index] ['selected'] = true;那麼它應該正常工作?但不起作用 –

+0

一旦我們將數據傳遞給子組件,它會保持自己的狀態,那麼它是如何改變父設備的狀態的呢? –

0

您是路過的引用,所以子組件具有從父組件

<AutoCompleteHlpr 
      dataSource={this.state.vehicles} 
      dataSourceConfig={dataSourceConfig} 
      floatingLabelText='Select Vehicles' 
      selectedOption={this.handleSelectedVehicle} 
/> 

你不應該發生變異,所以我會建議使用Object.assign([],[this.state.vehicles])訪問相同的車陣,如果它沒有嵌套下來或使用slice這是非破壞性的只傳遞dataSource支持它的副本,如dataSource = this.state.vehicles.slice(),也有JSON.parse(JSON.stringify(someobject))選項,這是最好的選擇原因是切片和Object.assign()都給你淺的原件副本目的。你還可以檢查出助手庫像lodash和這樣

注意:所有國家操縱只能使用this.setState作爲文檔here規定進行。檢查你的請求方法。

+0

dataSource = this.state.vehicles.slice(),not working –

+0

我已經解釋了爲什麼Object.assign([],[this.state.vehicles])和dataSource = this.state.vehicles.slice() '不會工作 –

+0

@AndrzejSmyk爲什麼他們不工作,如果狀態被正確操縱? –