2016-11-06 31 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> 
    ); 
} 
相关问题