2016-11-06 99 views
0

快速和「簡單」:如何讓我的父組件等待某個特定的狀態,直到我每次訪問父項時渲染子組件(不僅一次)?React Flux - 渲染子組件前等待道具

更詳細一些: 我希望能夠根據用戶輸入過濾結果。我有一個控制器視圖「Items」,它調用一個名爲「FilterComponent」的子組件,並將過濾器狀態作爲道具傳遞。我首先從控制器調用一個動作創建器,該動作創建器從本地存儲獲取已保存的過濾器並將其分發到商店。基於這個結果,我更新了過濾器狀態並將其作爲道具傳遞給了孩子。

我陷入困境的地方是找到一種方法來確保在設置過濾器狀態之前不會呈現「FilterComponent」。我可以使它工作,這樣當我刷新頁面時,它會加載過濾器 - 但是當我訪問其他地方的頁面並導航到Items控制器時,它會卡住。

我認爲它與我通過action/store獲取過濾器的方式有關,它在componentDidMount()中調用 - 因爲componentDidMount()只被調用一次,而且我需要調用它每次。

import React, { PropTypes } from 'react'; 
import FilterAction from '../../Actions/FilterActions'; 
import BaseComponent from '../../Components/Base'; 
import { Form, FormGroup, FormControl, ControlLabel, InputGroup, DropdownButton, MenuItem, Button } from 'react-bootstrap'; 
import Select from 'react-select'; 
import _ from 'lodash'; 

class FilterComponent extends BaseComponent { 
    constructor(props) { 
     super(props); 

     this.state = {}; 

     this._bind(
      'handleExperienceChange', 
      'handlePriceChange', 
      'applyFilter' 
     ); 
    } 

    componentDidMount() { 
     this.applyFilter(); 
    } 

    handlePriceChange(price) { 
     var filter = {}; 
     var product = this.props.product.tag; 

     filter[product] = { 
      price: price ? price.value : null 
     }; 

     let updatedState = _.merge(this.state, { 
      selectedPrice: price || null 
     }); 

     this.updateFilter(filter); 

     this.setState(updatedState); 
    } 

    getPrice() { 
     var price = this.props.lang.select.prices; 
     var priceSelect = []; 

     Object.keys(price).map(function (key) { 
      priceSelect.push({ 
       value: key, 
       label: price[key] 
      }); 
     }); 

     return priceSelect; 
    } 

    applyFilter() { 
     var filters = this.props.filters || null; 
     var product = this.props.product || null; 

     if (filters && product && filters[product.tag]) { 
      var selectedPrice = filter.price ? { 
       value: filter.price, 
       label: this.props.lang.select.prices[filter.price] 
      } : false; 
     } 

     var updatedState = { 
      selectedPrice: selectedPrice || false 
     }; 
     this.setState(_.merge(this.state, updatedState)); 
    } 

    updateFilter(filter) { 
     FilterAction.update(filter); 
    } 

    render() { 
     const { product, lang } = this.props; 

     return (
      <div className="filter"> 
       <div className="flex"> 
        <div className="pane-50"> 
         <Select name="price" 
           placeholder={lang.general.form.input.price} 
           value={this.state.selectedPrice} 
           options={this.getPrice()} 
           onChange={this.handlePriceChange} 
           searchable={false} /> 
        </div> 
       </div> 
      </div> 
     ); 
    } 
} 

export default FilterComponent; 
+0

你能不能在'Items' render方法中檢查這個狀態的存在,並且只有在狀態被設置時才包含'FilterComponent'作爲子對象?像(在jsx中)'{this.state.filters? :'Loading ...'}' –

+0

感謝您的迴應Bill!當我在該特定頁面上進行瀏覽器刷新時,它可以正常工作,但當我在其他地方進入頁面並導航到項目頁面時,它在「正在加載...」時沒有任何錯誤。也許我的孩子的組成部分是問題?我已經更新了我的答案,幷包含了我的子組件的代碼。我還注意到,當我點擊刷新時,它會在父級調用render()3次,而第三個元素設置了過濾器,但是當我從另一個頁面導航到它時,它只渲染兩次,兩次都過濾未定義。 – Dave

+0

你的代碼似乎在混合和匹配'this.props.filters'和'this.state.filters',這對我來說沒有意義。在'updateFilters'中,你設置了'this.state.filters',但你永遠不會在任何地方使用該狀態;你在'applyFilter'中使用'this.props.filters'。這可能是你的問題嗎? –

回答

1

您是否收到任何錯誤?變量filter似乎未在您的applyFilter()中定義。

此外,雖然無關,您可以通過調用setState()新鍵和值設置狀態,不必創建整個新狀態:

相反的:

var updatedState = { 
    selectedPrice: selectedPrice || false 
}; 
this.setState(_.merge(this.state, updatedState)); 

你可以簡單地做:

this.setState({ 
    selectedPrice: selectedPrice || false 
}); 

最後,考慮componentWillMount()調用applyFilter(),因爲它影響的組件是如何rendere d。在render()之前調用componentWillMount()

爲了獲得更快的幫助,請考慮在codepen或類似的服務上創建一個簡化的,孤立但可行的項目示例。

+0

感謝您的回覆squall3d!我似乎已經找出問題所在。我曾經呼籲我的動作創建者在'ChangeListener'之前獲取現有過濾器,導致它無法監聽來自商店的更改。我還必須在我的''FilterComponent''中使用'componentDidUpdate()',以確保它在我切換產品時重新渲染,而不僅僅是整頁重新加載。關於你從'render()'調用'applyFilter'的建議 - 我認爲調用任何在render()中改變狀態的東西是不好的做法? – Dave

+0

嗯,沒錯,用我手機上格式不好的地方誤解了它。我會刪除那些廢話。 – squall3d

1

如果你的狀態沒有準備好,只需從你的render()方法返回null即可。每當狀態或道具更新時,render()將再次運行。一旦你的狀態準備好了,像平常一樣返回你的子組件。

render() { 
    const { product, lang } = this.props; 

    if (!this.state.selectedPrice) 
     return null; 

    return (
     <div className="filter"> 
      <div className="flex"> 
       <div className="pane-50"> 
        <Select name="price" 
          placeholder={lang.general.form.input.price} 
          value={this.state.selectedPrice} 
          options={this.getPrice()} 
          onChange={this.handlePriceChange} 
          searchable={false} /> 
       </div> 
      </div> 
     </div> 
    ); 
}