2016-10-30 21 views
1

我正在使用React & Redux構建天氣應用程序。我決定嘗試冒險進入未知的水域,作爲React & Redux的noob。我將事情分解成表示組件和它們各自的容器來處理數據。儘管如此,我還是遇到了一些問題。這可能歸結爲我正在努力做到這一點,我只是非常不確定。React + Redux:將演示文稿與數據分開

現在我有SearchBarCurrentWeather,& Forecast組件和AppContainer,我試圖向這些組件集成到。目前爲止,我已將SearchBar組件集成到AppContainer中,並且沒有任何問題。這是我感到困惑的地方。所以我向容器提供了所需的操作和組件,並且容器已經是connected,所以當用戶執行搜索時,將會進行api調用並且狀態將通過reducer進行更新。

該數據應該可以通過mapStateToProps現在正確嗎?

如何在用戶執行操作後使用該數據,但不讓它在初始渲染時使用?如果AppContainer渲染這三個組件,我顯然會傳遞道具給它們,所以它們的渲染和功能就像它們預期的那樣。我認爲這是一個生命週期可以使用的地方,我只是不確定使用它們的方式或方式。我的代碼爲AppContainerSearcBar,& CurrentWeather以下。 CurrentWeather & Forecast幾乎是相同的(僅爲api提供不同端點的不同數據),所以我沒有提供它。我也沒有提供這些行爲或減法器,因爲我知道它們在我決定嘗試重構之前工作正常。也許我需要一個以上的容器來解決這個問題?任何建議或方向將不勝感激,謝謝大家,並有一個愉快的夜晚。

**是否有一個方面的問題:在_weatherSearch我有event.preventDefault();,因爲SearchBar是一個表單元素。我甚至需要提供這個嗎?如果event不是什麼通過,但term我認爲沒有。所述event被用作在SearchBar形式元件下方觀察:

onSubmit={event => getWeather(event.target.value)}

應用容器:

import React, { Component } from 'react'; 
import { connect } from 'react-redux'; 
import { fetchCurrentWeather, fetchForecast } from '../actions/actions'; 

import SearchBar from '../components/SearchBar'; 
import CurrentWeather from '../components/CurrentWeather'; 

class AppContainer extends Component { 

    _weatherSearch(term) { 
    event.preventDefault(); 
    // Here is where we go to fetch weather data. 
    this.props.fetchCurrentWeather(term); 
    this.props.fetchForecast(term); 
    } 

    render() { 
    const getWeather = term => {this._weatherSearch(term);}; 
    return (
     <div className="application"> 
     <SearchBar getWeather={getWeather}/> 
     <CurrentWeather /> 
     </div> 
    ); 
    } 
} 

const mapStateToProps = ({ current, forecast }) => { 
    return { 
    current, 
    forecast 
    } 
} 

export default connect(mapStateToProps, 
    { fetchCurrentWeather, fetchForecast })(AppContainer); 

搜索欄:

import React from 'react'; 

const SearchBar = ({ getWeather }) => { 
    return(
    <form className='input-group' onSubmit={event => getWeather(event.target.value)}> 
     <input 
     className='form-control' 
     placeholder='Search a US City' /> 
     <span className='input-group-btn'> 
     <button className='btn btn-secondary' type='submit'>Submit</button> 
     </span> 
    </form> 
); 
} 

export default SearchBar; 

CurrentWeather:*注意:我有沒有從CurrentWeather中刪除任何邏輯或數據處理,但它沒有被重構紅色只是一個表象的唯一組件。

import React, {Component} from 'react'; 
import {connect} from 'react-redux'; 
import {unitConverter} from '../conversions/conversions_2.0'; 

export class CurrentWeather extends Component { 
    _renderCurrentWeather(cityData) { 
     const name = cityData.name; 
     const {temp, pressure, humidity} = cityData.main; 
     const {speed, deg} = cityData.wind; 
     const {sunrise, sunset} = cityData.sys; 
     return (
      <tr key={name}> 
       <td>{unitConverter.toFarenheit(temp)} F</td> 
       <td>{unitConverter.toInchesHG(pressure)}"</td> 
       <td>{humidity}%</td> 
       <td>{unitConverter.toMPH(speed)}mph {unitConverter.toCardinal(deg)}</td> 
      </tr> 
     ); 
    } 

    render() { 
     let currentWeatherData = []; 
     if (this.props.current) { 
      currentWeatherData = this.props.current.map(this._renderCurrentWeather); 
     } 
     return (
      <table className="table table-reflow"> 
       <thead> 
        <tr> 
         <th>Temperature</th> 
         <th>Pressure</th> 
         <th>Humidity</th> 
         <th>Wind</th> 
        </tr> 
       </thead> 
       <tbody> 
        {currentWeatherData} 
       </tbody> 
      </table> 
     ); 
    } 
} 

function mapStateToProps({current}) { 
    return {current}; 
} 

export default connect(mapStateToProps)(CurrentWeather); 

回答

2

您的渲染功能是非常動態的。你可以省略任何你喜歡的東西:

class AppContainer extends Component { 

    _weatherSearch(term) { 
    // event.preventDefault(); We can't do this because we don't have an event here... 

    this.props.fetchCurrentWeather(term); 
    this.props.fetchForecast(term); 
    } 

    render() { 
    const getWeather = term => { this._weatherSearch(term); }; 
    return (
     <div className="application"> 
     <SearchBar getWeather={getWeather}/> 
     { Boolean(this.props.current) && <CurrentWeather /> } 
     </div> 
    ); 
    } 
} 

const mapStateToProps = ({ current }) => ({ current }); 

export default connect(mapStateToProps, 
    { fetchCurrentWeather, fetchForecast })(AppContainer); 

這就是你如何處理缺失的數據。你只要不顯示任何內容,或者首先搜索消息,或者如果它正在加載,你可以顯示一個微調器或悸動者。

上面用來隱藏CurrentWeather的技術是,如果我們想要隱藏組件,則將布爾值傳遞給React。 React忽略了true,false,nullundefined

請注意,最好只傳遞mapStateToProps中的數據,您將在組件本身中實際使用這些數據。在你的代碼中,你傳遞的是currentforecast,但是你不使用它們。

當任何mapStateToProps,mapDispatchToPropsprops數據發生變化時,Redux將重新提交。通過返回數據,您永遠不會使用指示Redux在沒有必要時重新提交。

2

我是一個反應 - 還原noob我自己:-),我遇到過類似的問題。

據我所知,你所做的容器/表象分離看起來不錯,但你可以去,甚至更進一步,分離容器的獲取安裝

我所指的解決辦法是什麼人不同稱之爲「高階組件」和「包裝部件」:(下面的代碼沒有進行測試 - 這只是爲了演示)

import {connect} from blah; 

const AppWrap = (Wrapped) => { 
    class AppWrapper extends Component { 
    constructor(props) { 
     super(props); 
     this.state = {foo: false}; 
    } 
    componentWillMount() { 
     this.props.actions.fooAction() 
     .then(() => this.setState({foo: false})); 
    } 
    render() { 
     return (<Wrapped {...this.props} foo={this.state.foo}/>); 
    } 
    } 

    function mapState(state) { blah } 
    function mapDispatch(dispatch) { blah } 

    return connect(mapState, mapDispatch)(AppWrapper); 
} 

export default AppWrap;  

注意頂部的= (Wrapped) => {部分。這就是實際的「包裝」,只要你在渲染鉤子中引用它,參數可以被命名爲任何東西。

現在在你的AppContainer中,你得到一個this.props.foo,它作爲一個標誌告訴你fooAction()已經完成,你可以用它來相應地呈現你的表示組件。在fooAction完成之前,您可以確定傳入AppContainer的foo將爲false

爲了把我剛纔說成代碼,您AppContainer可能是這個樣子:

import AppWrapper from './AppWrapper'; 

class AppContainer extends Component { 
    constructor(props) { 
    super(props); 
    } 
    render() { 
    return (!this.props.foo) ? <div>bar</div> : (
     <div blah> 
     <SearchBar blah/> 
     <CurrentWeather blah/> 
     </div> 
    ); 
    } 
} 

export default AppWrapper(AppContainer); 

使用封裝組件這樣的好處是,

  • 你可以採取更多的控制瞭解數據如何以及何時完成呈現
  • 解釋「加載」機制和邏輯
  • 避免像必須製作這樣的古怪問題鉤子,不得不處理the consequences

看看這個博客帖子,瞭解更多有關肝卵圓細胞:https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750

+0

做你的'AppWrapper'包含表象成分的邏輯,然後你只是傳遞'props'到所呈現的子組件在'AppContainer'中? – rockchalkwushock

+0

我猜你在哪裏放置邏輯是一個優惠的事情。我正在考慮一切,就像你之前做的一樣,除了你取出異步操作,把它們放到'AppWrapper'中,並將這些操作的「狀態」變成一個道具。所以總之,包裝器包含異步動作,容器包含邏輯,組件包含jsx。 – omul