2015-10-06 37 views
16

我是新来的反应和流量,我很难弄清楚如何从服务器加载数据。我可以从本地文件加载相同的数据,而不会出现问题。如何在REACT和FLUX中创建API调用

信息首先我有向下传递初始状态的视图此控制器视图(控制器view.js)(view.js)

控制器view.js

var viewBill = React.createClass({ 
getInitialState: function(){ 
    return { 
     bill: BillStore.getAllBill() 
    }; 
}, 
render: function(){ 
    return (
     <div> 
      <SubscriptionDetails subscription={this.state.bill.statement} /> 
     </div> 
    ); 
} 
}); 
module.exports = viewBill; 

view.js

var subscriptionsList = React.createClass({ 
propTypes: { 
    subscription: React.PropTypes.array.isRequired 
}, 
render: function(){ 

    return (
     <div > 
      <h1>Statement</h1> 
      From: {this.props.subscription.period.from} - To {this.props.subscription.period.to} <br /> 
      Due: {this.props.subscription.due}<br /> 
      Issued:{this.props.subscription.generated} 
     </div> 
    ); 
} 
}); 
module.exports = subscriptionsList; 

我有一个行动文件加载INITAL数据为我的应用程序。因此,这是数据是由用户操作,调用,但在控制器视图从称为getInitialState

InitialActions.js

var InitialiseActions = { 
initApp: function(){ 
    Dispatcher.dispatch({ 
     actionType: ActionTypes.INITIALISE, 
     initialData: { 
      bill: BillApi.getBillLocal() // I switch to getBillServer for date from server 
     } 
    }); 
} 
}; 
module.exports = InitialiseActions; 

然后我的数据API看起来像这样

api.js

var BillApi = { 
getBillLocal: function() { 
    return billed; 
}, 
getBillServer: function() { 
    return $.getJSON('https://theurl.com/stuff.json').then(function(data) { 

     return data; 
    }); 
} 
}; 
module.exports = BillApi; 

这是店里因此,正如我前面提到的,当我加载数据在本地使用BillApi.getBillLocal()的行动,一切工作正常 store.js

var _bill = []; 
var BillStore = assign({}, EventEmitter.prototype, { 
addChangeListener: function(callback) { 
    this.on(CHANGE_EVENT, callback); 
}, 
removeChangeListener: function(callback) { 
    this.removeListener(CHANGE_EVENT, callback); 
}, 
emitChange: function() { 
    this.emit(CHANGE_EVENT); 
}, 
getAllBill: function() { 
    return _bill; 
} 
}); 

Dispatcher.register(function(action){ 
switch(action.actionType){ 
    case ActionTypes.INITIALISE: 
     _bill = action.initialData.bill; 
     BillStore.emitChange(); 
     break; 
    default: 
     // do nothing 
} 
}); 

module.exports = BillStore; 

。但是,当我改变BillApi.getBillServer()我得到在控制台中followind错误...

Warning: Failed propType: Required prop `subscription` was not specified in  `subscriptionsList`. Check the render method of `viewBill`. 
Uncaught TypeError: Cannot read property 'period' of undefined 

我还添加了的console.log(数据)BillApi.getBillServer(),我可以看到,数据从服务器返回。但它显示AFTER我得到的控制台,我认为可能是问题的警告。任何人都可以提供一些建议或帮助我解决它?对不起,这么长的帖子。

UPDATE

我做了一些更改api.js文件(点击此处变革和DOM错误plnkr.co/edit/HoXszori3HUAwUOHzPLG),因为它是建议,这个问题是由于我如何处理承诺。但它仍然是你在DOM错误中看到的同样的问题。

+0

你在传递什么'subscriptionsList'?它正在寻找'this.props.subscriptions',并且它不存在,所以你得到'不能读取'undefined'属性'时期'。我的猜测是你也有一些类型的竞争条件。通量本质上是异步的... –

+0

我想也许多数民众赞成为什么我得到'无法读取'的错误 - 由于竞争条件。数据可能尚未加载?任何提示如何解决这个问题? –

+1

是的,你可以使用superlame建议的回调方法,或者你可以给'_bill'一个默认对象,比如'var _bill = {subscriptions:[]}',所以当你执行'getInitialState'时,你只需要'bill'通过'store.getAllBill()'。那么当组件装入时,数据被提取,并且商店将发出更改并更新您的状态 –

回答

8

这是一个异步问题。使用$.getJSON().then()是不够的。由于它返回一个承诺的对象,你必须做一些像api.getBill().then(function(data) { /*do stuff with data*/ });

以处理调用的承诺我做了一个CodePen example用下面的代码:

function searchSpotify(query) { 
    return $.getJSON('http://ws.spotify.com/search/1/track.json?q=' + query) 
    .then(function(data) { 
    return data.tracks; 
    }); 
} 

searchSpotify('donald trump') 
.then(function(tracks) { 
    tracks.forEach(function(track) { 
    console.log(track.name); 
    }); 
}); 
+0

感谢您的回答。但是,如果您检查api.js在哪里使用getBillServer调用服务器,您可以看到我正在使用.then()并将数据返回给操作。这是不正确的? –

+0

更新了我的答案。基本上,第一个'.then()'返回一个promise,所以你必须使用另一个'.then()'来获取你的数据。 – ultralame

+0

非常感谢。我现在只是在尝试。要明确一点,在这种情况下,searchSpotify函数会进入我的api.js文件和函数调用(searchSpotify('donald trump'))到我的动作文件中? –

2

另一种方法是检查道具的订阅存在,然后再玩数据。

尝试修改你的代码看起来有点像这样:

render: function(){ 

    var subscriptionPeriod = ''; 
    var subscriptionDue = ['','']; 
    var subscriptionGenerated = ''; 

    if(this.props.subscription !== undefined){ 
     subscriptionPeriod = this.props.subscription.period; 
     subscriptionDue = [this.props.subscription.due.to,this.props.subscription.due.from]; 
     subscriptionGenerated = this.props.subscription.generated; 
    } 

    return (
    <div > 
     <h1>Statement</h1> 
     From: {subscriptionPeriod[0]} - To {subscriptionPeriod[1]} <br /> 
     Due: {subscriptionDue}<br /> 
     Issued:{subscriptionGenerated} 
    </div> 
); 
} 

在返回之前渲染功能尝试添加以下内容: 如果(!this.props.subscription =未定义){// 在这里做些什么 }

由于您的数据改变了顶层组件的状态,所以一旦它具有定义了订阅道具的数据,它将重新触发渲染。

+0

谢谢。我会在哪里放置这段代码?我认为它不应该在控制器视图中包装渲染功能? –

+0

请参阅我对代码的修改 –

+0

非常感谢您编写代码。我可以看到这是一个更好的方法。但问题仍然存在于初始数据未加载的位置。我仍然收到错误“Warning:失败的propType:必需的prop'subscription'没有在'subscriptionsList'中指定。检查'viewBill'的渲染方法。”我猜在数据加载和重新触发没有发生时状态不会改变。我可以看到数据已加载到控制台窗口中。任何想法可能会导致它? –

2

如果我理解正确的话,你可以用一些尝试这样

// InitialActions.js 


var InitialiseActions = { 
initApp: function(){ 
    BillApi.getBill(function(result){ 
     // result from getJson is available here 
     Dispatcher.dispatch({ 
      actionType: ActionTypes.INITIALISE, 
      initialData: { 
       bill: result 
      } 
     }); 
    }); 
} 
}; 
module.exports = InitialiseActions; 

//api.js 

var BillApi = { 
    getBillLocal: function() { 
     console.log(biller); 
     return biller; 
    }, 
    getBill: function(callback) { 
     $.getJSON('https://theurl.com/stuff.json', callback); 
    } 
}; 

$ .getJSON不从http请求返回值。它使它可用于回调。 这背后的逻辑在这里详细解释:How to return the response from an asynchronous call?

3

它看起来像你的代码,预期流动是一样的东西:

  • 一些组件火灾初始化动作,
  • 初始化操作调用API
  • 等待服务器的结果(我认为这里是事情发生的地方:你的组件渲染在服务器返回结果之前开始),
  • 然后将结果传递给商店,
  • 发出变化并且
  • 触发重新呈现。

在一个典型的流量设置,我会建议构建这个有点不同:

  • 一些组件调用API(但不会触发行动调度员尚未
  • API做getJSON并等待服务器结果
  • 只有收到结果后,API触发与接收到的数据
  • 店响应行动,并与结果更新自身的初始化动作
  • 然后发出变化
  • 触发重新渲染

我不是那么熟悉的jQuery ,承诺和链接,但我认为这大致会转化为您的代码中的以下更改:

  • 控制器视图需要更改侦听器到商店:添加一个componentDidMount()将事件侦听器添加到磁通存储更改的功能。
  • 在控制器视图中,事件侦听器触发一个setState()函数,该函数从存储中获取最新的_bill。
  • dispatcher.dispatch()从您的actions.js移动到您的api.js(替换return data);

这样一来,你的组件最初应该使一些“装载”的消息,并尽快更新来自服务器的数据是。

2

我会分开我的动作,存储和访问量(反应的组分)。

首先,我会实现我的操作是这样的:

import keyMirror from 'keymirror'; 


import ApiService from '../../lib/api'; 
import Dispatcher from '../dispatcher/dispatcher'; 
import config from '../env/config'; 


export let ActionTypes = keyMirror({ 
    GetAllBillPending: null, 
    GetAllBillSuccess: null, 
    GetAllBillError: null 
}, 'Bill:'); 

export default { 
    fetchBills() { 
    Dispatcher.dispatch(ActionTypes.GetAllBillPending); 

    YOUR_API_CALL 
     .then(response => { 
     //fetchs your API/service call to fetch all Bills 
     Dispatcher.dispatch(ActionTypes.GetAllBillSuccess, response); 
     }) 
     .catch(err => { 
     //catches error if you want to 
     Dispatcher.dispatch(ActionTypes.GetAllBillError, err); 
     }); 
    } 
}; 

下一个是我的店,这样我就可以跟踪所有的变化突然可能我的API调用过程中发生:

class BillStore extends YourCustomStore { 
    constructor() { 
    super(); 

    this.bindActions(
     ActionTypes.GetAllBillPending, this.onGetAllBillPending, 
     ActionTypes.GetAllBillSuccess, this.onGetAllBillSuccess, 
     ActionTypes.GetAllBillError , this.onGetAllBillError 
    ); 
    } 

    getInitialState() { 
    return { 
     bills : [] 
     status: Status.Pending 
    }; 
    } 

    onGetAllBillPending() { 
    this.setState({ 
     bills : [] 
     status: Status.Pending 
    }); 
    } 

    onGetAllBillSuccess (payload) { 
    this.setState({ 
     bills : payload 
     status: Status.Ok 
    }); 
    } 

    onGetAllBillError (error) { 
    this.setState({ 
     bills : [], 
     status: Status.Errors 
    }); 
    } 
} 


export default new BillStore(); 

最后,您的组件:

import React from 'react'; 

import BillStore from '../stores/bill'; 
import BillActions from '../actions/bill'; 


export default React.createClass({ 

    statics: { 
    storeListeners: { 
     'onBillStoreChange': BillStore 
    }, 
    }, 

    getInitialState() { 
    return BillStore.getInitialState(); 
    }, 

    onBillStoreChange() { 
    const state = BillStore.getState(); 

    this.setState({ 
     bills : state.bills, 
     pending: state.status === Status.Pending 
    }); 
    }, 

    componentDidMount() { 
    BillActions.fetchBills(); 
    }, 

    render() { 
    if (this.state.pending) { 
     return (
     <div> 
      {/* your loader, or pending structure */} 
     </div> 
    ); 
    } 

    return (
     <div> 
     {/* your Bills */} 
     </div> 
    ); 
    } 

}); 
0

假设你实际上是从你的API获取数据,但得到它为时已晚,首先将错误抛出,试试这个: 在你的控制器view.js,添加以下内容:

componentWillMount: function() { 
    BillStore.addChangeListener(this._handleChangedBills); 
}, 

componentWillUnmount: function() { 
    BillStore.removeChangeListener(this._handleChangedBills); 
}, 

_handleChangedBills =() => { 
    this.setState({bill: BillStore.getAllBill()}); 
} 

而在你getInitialState功能,可以用一个空物体的结构,你的代码需要(具体来说,在其中有一个'语句'对象)。事情是这样的:

getInitialState: function(){ 
return { 
    bill: { statement: [] } 
}; 
}, 

正在发生的事情是,当你得到你的初始状态,它不是从商店获取正确的,所以会返回一个未定义的对象。然后,当你询问this.state.bill.statement时,bill被初始化但未定义,所以它找不到任何叫做statement的东西,所以你需要添加它。在组件有更多时间之后(这是一个像其他海报说的异步问题),它应该从商店中正确取货。这就是我们等待商店为我们发布变更的原因,然后我们从商店中获取数据的原因。

相关问题