2017-10-09 127 views
0

我今晚一直在圍繞使用道具來設置組件的初始狀態這個話題進行一些研究,並且我遇到了爭論雙方的人。因此,我的問題有兩個部分。使用道具來初步設置狀態

1)我在做什麼考慮反模式?從我可以告訴它不是 - 根據this article如果是這樣,它有什麼特別的錯誤?

2)有沒有另外一種方法可以在不使用道具設置狀態的情況下重寫這個邏輯?

父組件:

class App extends Component { 

    constructor(props){ 
    super(props); 
    this.state ={ 
     todos: [] 
    } 
    } 

    componentDidMount() { 
    axios.get('https://jsonplaceholder.typicode.com/todos') 
    .then(response => { 
     this.setState({ todos: response.data }) 
    }); 
    } 

    render() { 
    if(!this.state.todos){ 
     return <div>Loading...</div> 
    } 
    return (
     <div className="container"> 
      <div className="row"> 
      { 
       this.state.todos.map((todo, i) => { 
       return (
        <Todo todo={todo} key={i}/> 
       ) 
       }) 
      } 
      </div> 
     </div> 
    ); 
    } 
} 

輔元件

class Todo extends Component{ 

    constructor(props) { 
    super(props); 
    var { title, completed, userId } = this.props.todo; 
    this.state = { title, completed, userId } 
    } 
    changeCompletion =() => { 
    this.setState({completed: !this.state.completed}) 
    } 

    render() { 
    return(
     <div className="col-md-4 col-sm-6"> 
     <div className={"card card-inverse text-center " + (this.state.completed ? 'card-success' : 'card-danger')}> 
      <div className="card-block"> 
      <blockquote className="card-blockquote"> 
       <p>{ this.state.title }</p> 
      </blockquote> 
      <button onClick={this.changeCompletion} className={"btn btn-sm " + (this.state.completed ? 'btn-danger' : 'btn-success')}>{ this.state.completed ? 'incomplete' : 'complete'} </button> 
      </div> 
     </div> 
     </div> 
    ) 
    } 
} 

回答

3

1。是我在做什麼被認爲是反模式? - 也許,是

的問題是你的孩子組成的構造唯一跨多個渲染運行一次。如果在子組件中使用setState()重新渲染,構造函數將不會再次運行,因此道具和狀態不同步,這是文章提到的「多個真相源」問題。

如果您重新呈現父組件(通過狀態更改,道具更改或this.forceUpdate()),您的子組件的構造函數將不會重新執行。也就是說,React可以在內部提高性能,您可以使用console.log()來調查組件的生命週期。

通常,如果我碰到你的情況,我會添加一個叫做ComponentWillReceiveProps(nextProps)的生命週期方法,並在那裏使用nextProps來重新啓動狀態。雖然它不是完美的解決方案,因爲其他開發人員可能會修改組件中的道具,但我們又回到了「多個真相源」問題。您需要告訴或教育項目中的其他開發人員,他們不允許在組件中直接或間接修改道具,但即使他們有時可能會忘記。但至少增加生命週期方法確實改善了這種情況。

2.有沒有另外一種方法可以在不使用道具來設置狀態的情況下重寫這個邏輯?

你可以嘗試這種方法:基本上,你在子組件的道具和父組件中公開一個回調,你執行它來觸發父組件的重新呈現,而這又會觸發重新呈現所有子組件這是我們想要的。

請記住,當React重新渲染所有子組件時,它不會破壞然後重新創建它們,因此它們的constructor將不會重新執行。只有他們的生命週期方法ComponentWillReceiveProps(nextProps)將被執行。

父組件:

<Todo todo={todo} key={i} onChangeCompletionCallback={() => { 
    let clonedState = Object.assign({}, this.state); 
    clonedState.todos[i].completed = !clonedState.todos[i].completed; 
    this.setState(clonedState); 
}}/>  

輔元件

changeCompletion =() => { 
    this.props.onChangeCompletionCallback(); 
} 

一個跟進的問題,在這個特殊的例子,因爲有 200待辦事項貝因g渲染,我只改變完成狀態 一次,這是一個實例,在手動配置 shouldComponentUpdate()函數可能是1 渲染與200的區別嗎?渲染所有200個待辦事項本身是不好的?

我們先來澄清一下render的術語。有2種在render陣營:

  • 真實DOM render
  • 虛擬DOM render:(的設計是)現在快速

,如果你真正的DOM render 200 todos,這絕對是不好的。但是,如果你虛擬DOM render 200 todos,這取決於。但大多數情況下,它會很快。

當你render一個組成部分通過props changestate changethis.forceUpdate(),你正在做一個虛擬DOM render。之後,

  1. React使用差異化算法比較真實DOM和虛擬DOM,然後提取差異。
  2. 根據以上差異,React制定了Real DOM render。它只有 真正的DOM renders需要什麼。

所以我不認爲在這種情況下手動配置shouldComponentUpdate(...)生命週期方法將有很大幫助。它看起來像你保存了199虛擬DOM renders但實際上你正在浪費你的努力。

this.setState(...)是異步的。這意味着每當您撥打this.setState(...)虛擬DOM render不會立即發生。 React將嘗試在一個大型虛擬DOM render中批量處理多個虛擬DOM renders。因此,即使您發出199個虛擬DOM renders,反應也足夠聰明,可以將它們一起批處理,因此只會發生1個虛擬DOM render。沒有必要在shouldComponentUpdate(...)中手動執行此操作。

最後,我的解釋是在理論上(基於文檔)。如果您認真對待性能優化問題,則可能需要進行調查才能獲得更爲可靠的信息(事實數字爲&)。但至少,我的理論解釋可以給你一個好的開始,我希望。

+0

很好的解釋!你的實現工作完美。我沒有考慮過使用回調,但可以看到這是一個有價值的工具。一個後續問題,在這個特定的例子中,因爲有200個待辦事項被渲染,而且我只是每次只改變一個完成狀態,這是否會是一個手動配置shouldComponentUpdate()函數可能是不同的實例1渲染與200?它本質上是_bad_渲染所有200 todos? – coloradocolby

+1

答案太大而不適合評論,因此我更新了上面的答案。請看一下。 –

+0

明白了。我想我可能是清楚的。我會做更多的挖掘,以確保我非常感謝你的幫助! – coloradocolby