2014-11-07 4 views
12

Я хочу сделать что-то вроде этогоFlux/React комплекса многоразового компонента

var App = React.createClass({ 
    render: function() { 
     return (
      <CountryAutoComplete /> 
     ) 
    } 
}); 

Различных приложений

var App2 = React.createClass({ 
    render: function() { 
     return (
      <CountryAutoComplete /> 
     ) 
    } 
}); 

Вот простое автозаполнение (не весь файл)

var AutoComplete = React.createClass({ 
    componentDidMount: function() { 
     $(this.getDOMNode()).typeahead(this.props); 
    }, 
    render: function() { 
     return (
      <input type="text" class="typeahead" onChange={this.props.onChange} /> 
     ); 
    } 
}); 

CountryAutoComplete был бы чем-то подобным, чтобы быть самодостаточным.

var CountryAutoComplete = React.createClass({ 
    search: function(country, process) { 
     // Make an ajax call and return the data. No other components needed 
     $.ajax({ 
      url: '/country' + '?search=' + country 
     }).then(process); 
    }, 
    render: function() { 
     return (
      <AutoComplete onChange={this.props.onChange} source={this.search} /> 
     ); 
    } 
}); 

Основываясь на Flux документации, это выглядит как что-либо с вызовом API должен пройти через действия

-> API -> Диспетчер -> Магазины -> компонент

Это делает CountryAutoComplete привязан к определенному приложению, потому что действия, Диспетчер и магазины специфичны для приложения. Каков наилучший способ сделать этот компонент многоразовым для всех приложений?

ответ

10

Вы не должны делать какие-либо вызовы ajax в компоненте автозаполнения (так как вы сказали, что хотите сделать его многоразовым). Я обычно помещаю все запросы на запрос данных/использование api в отдельный модуль, который использует обещания для предотвращения множественных запросов.

Итак, идея состоит в том, чтобы только ваш компонент автозаполнения получал параметры/данные от родительского компонента. Этот родительский компонент может сначала получить данные из хранилища, а также прослушать любые события изменений в этом хранилище и, при необходимости, обновить его состояние. Передайте это this.state.options (или независимо от того, какое состояние вы используете для параметров) в качестве опоры для автозаполнения. Когда пользователь что-то набирает, выпустите действие с запросом. Это действие по очереди должно вызывать API и диспетчер, обновлять хранилище и испускать событие изменения для хранилища. Ваш родительский компонент будет обновлять свое состояние соответственно, и это будет передаваться компоненту AutoComplete в качестве опоры.

Так что-то вроде этого:

var App = React.createClass({ 
    getInitialState: function() { 
     return { 
      // default results/data? 
      data : Store.getResults('') 
     }; 
    }, 
    storeChangeListener: function(newData) { 
     this.setState({ 
      data: newData 
     }); 
    }, 
    componentDidMount: function() { 
     this.listenTo(Store, this.storeChangeListener); 
    }, 
    onChange: function(query) { 
     // on search change 
     Actions.getResults(query); 
    }, 
    render: function() { 
     return (
      <AutoComplete data={this.state.data} onChange={this.onChange} /> 
     ); 
    } 
}); 

И в магазине, что-то вроде этого:

var countryAPI = require('./countryAPI') 
var Store = { 
    getResults: function(query) { 
     // check cache if any? otherwise make call 
     if(this.cache[query]) { 
      return this.cache[query]; 
     } else { 
      countryAPI.search(query).then(this.update); 
     } 
    }, 
    update: function(data) { 
     AppDispatcher.dispatch({ 
      type: "DATA_FROM_SERVER", 
      payload: {id: query, data: result} 
     }) 
    }, 
    handleDataFromServer: function(action) { 
     //store into cache/store 
     this.cache[action.payload.id] = action.payload.result; 
     this.emit("change"); // re-render app on whoever is listening to this store 
    } 
} 

и ваш апи, например

var countryAPI = { 
    search: function(query) { 
     // check to make sure this promise isnt called before 
     if(!this.allPromises[query]) { 
      this.allPromises[query] = $.ajax({ 
       url: '/country' + '?search=' + country 
      }) 
     } 
     return this.allPromises[query]; 
    } 
} 

Подводя итог, фактическая Внедрение API imo должно быть отделено от действий потока, они должны быть связаны только с конкретными вещами Web-API и d просто разрешите действия потока/хранилища обрабатывать ответы отдельно в потоке данных:

Component --> Listens to Store 
      --> Calls Load Action --> Show Pending State/Optimistic Updates --> Dispatcher --> Store --> changeEvent (Component will be listening and be updated) 
      --> countryAPI.load() 
onLoadSuccess --> Dispatcher --> Store --> changeEvent --> Component 
onLoadError --> Dispatcher --> Store --> changeEvent --> Component 
+1

Спасибо за подробный ответ. Это было очень полезно. Компоненты не предполагают, чтобы напрямую поддерживать API или API вызовов? Кажется, что это очень много плиты котла, чтобы использовать этот компонент каждый раз. Диспетчер является специфическим для приложения. –

+2

хорошо реагировать - это взгляд, поэтому любые запросы ajax (вызовы api), на мой взгляд, должны быть отделены от реагирующих компонентов. это может быть много плиты котла, но он удерживает компонент повторно и изолирован. Вот более выписанная статья о потоке данных: http://www.code-experience.com/async-requests-with-react-js-and-flux-revisited/ – trekforever

+0

Поскольку автозаполнение полагается на ajax , а ajax принадлежит отдельному компоненту/диспетчеру, любой компонент, который полагается на ajax, действительно не может быть независимым, повторно используемым компонентом? Что было бы неправильно с тем, что ajax содержится в компоненте автозаполнения и что компонент излучает события, которые могут прослушивать диспетчер приложений и хранилище? – jczaplew

Смежные вопросы