2016-11-06 6 views
0

Быстрое и «легкое»: как заставить мой родительский компонент ждать определенного состояния, пока я не буду рендерить дочерний компонент каждый раз, когда я получаю доступ к родительскому объекту (не только один раз)?React Flux - дождаться реквизита перед рендерингом дочерних компонентов

Немного подробнее: Я хочу, чтобы иметь возможность фильтровать результаты на основе ввода пользователем. У меня есть представление контроллера «Элементы», которое вызывает дочерний компонент «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' и включить только фильтр FilterComponent в качестве дочернего элемента, если оно установлено? Как (в jsx) '{this.state.filters? : 'Загрузка ...'} ' –

+0

Спасибо за ваш ответ Билл! Он работает, когда я обновляю браузер на этой конкретной странице, но он застревает без ошибок в «Загрузка ...», когда я вхожу на страницу где-то еще и перехожу к странице элементов. Может быть, мой детский компонент - проблема? Я обновил свой ответ и включил код моего дочернего компонента. То, что я также заметил, это то, что когда я нажимаю refresh, он вызывает render() в родительском 3 раза, а третий имеет набор фильтров, однако, когда я перемещаюсь с другой страницы на него, он отображает только два раза и оба раза фильтрует не определено. – Dave

+0

Ваш код, кажется, смешивает и сопоставляет 'this.props.filters' и' this.state.filters' таким образом, который для меня не имеет смысла. В 'updateFilters' вы устанавливаете' this.state.filters', но никогда не используете его нигде; вы используете 'this.props.filters' в' applyFilter'.Это может быть ваша проблема? –

ответ

1

У вас возникли ошибки? Переменная filter кажется неопределенной в вашем applyFilter().

Кроме того, хотя и не связанные, вы можете установить состояние, вызвав setState() с новыми ключами и значениями, не должны создавать все новое состояние:

Вместо:

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

Вы можете просто сделать:

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

Наконец, рассмотрим вызова applyFilter() из componentWillMount(), так как это влияет как компонент rendere д. componentWillMount() вызывается до render().

Чтобы получить более быструю помощь, рассмотрите возможность создания упрощенного, изолированного, но рабочего примера вашего проекта на кодовых или подобных сервисах.

+0

Спасибо за ваш ответ squall3d! Кажется, я понял, в чем проблема. Я назвал своего создателя действия для получения существующих фильтров до 'ChangeListener', из-за чего он не смог прослушать изменения из магазина. Я также должен был использовать 'componentDidUpdate()' внутри my'FilterComponent', чтобы убедиться, что он повторно отображает, когда я переключаю продукты, а не только на полную перезагрузку страницы. Что касается вашего предложения, вызывающего 'applyFilter' из' render() '- я думал, что это была плохая практика, вызывающая что-либо, изменяющее состояние в' render() '? – Dave

+0

Ах, верно, неправильно прочитайте его с плохим форматированием на моем мобильном телефоне. Я уберу эту глупость. – squall3d

1

Если ваше состояние не готово, просто верните null из метода render(). Когда состояние или реквизит обновляется, 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> 
    ); 
}