简体   繁体   中英

General State Questions with ReactJS & Reflux

I'm pretty new to React and am trying to wrap my mind around Reflux.

Here's my scenario:

I currently have a very simple app that is calling some song data from a rest api, once retrieved, it is being stored in state.songs (this is happening within an Init function in the song store):

var SongStore = Reflux.createStore({
  listenables: [SongActions],

  getInitialState: function() {
    return []
  },

  init: function() {
    request('http://127.0.0.1:1337/api/songs', function(error, response, body) {
      if (!error && response.statusCode == 200) {
        var content = JSON.parse(body);
        this.trigger(content);
      }
    }.bind(this));
  },

  onHandleClick: function(album_link) {
  // this.state.songs is undefined here??
  }

 });

Here's the component that's rendering the song data:

var Home = React.createClass({

mixins: [Reflux.connect(SongStore, 'songs')],

render: function() {
  return (
    <div className={'react-app-home'}>
      <div className="float-left">
        <div className="song-data">
          {this.state.songs.map(function(song) {
            return <div><p>{song.lyrics}</p></div>
          })}
        </div>
      </div>
      <div className="float-right">
        <AlbumList />
      </div>
    </div>
  );
 }
});

This is all working as intended.

I also have a list of albums, which is rendered in it's own component (and has it's own store), and i'm trying to hook up a click function on an album item, so once an album title is clicked, this.state.songs is filtered and the songs component is re-rendered.

The issue i'm having is when I try to access this.state.songs from the song store, it's undefined (see onHandleClick func in song store above).

Album list component:

var AlbumList = React.createClass({
  mixins: [Reflux.connect(AlbumStore, 'albums'),        
  Reflux.connect(SongStore, 'songs')],
  render: function() {
    return (
      <div className="album-list">
        {this.state.albums.map(function(album) {
        return <p onClick={SongStore.onHandleClick.bind(null, album.album_link)}>{album.album_name}</p>
        })}
      </div>
    );
  }
});

Album list store:

var AlbumStore = Reflux.createStore({
  listenables: [AlbumActions],
  getInitialState: function() {
    return [];
  },

  onReload: function() {
    request('http://0.0.0.0:1337/api/albums', function(error, response, body) {
      if (!error && response.statusCode == 200) {
        var content = JSON.parse(body);
        this.trigger(content);
      }
    }.bind(this));
  },

  init: function() {
    this.onReload();
  }
});

Can anyone help me out? An answer with some explaining would be awesome so I can understand what the problem is.

Thanks!

You should be using actions for handleClick or any other call from the component to the store. I would also suggest separating your api calls from the store. Here is an example from https://github.com/calitek/ReactPatterns React.14/ReFluxSuperAgent.

app.js

 'use strict'; import React from 'react'; import ReactDom from 'react-dom'; import AppCtrl from './components/app.ctrl.js'; import Actions from './actions/api.Actions'; import ApiStore from './stores/Api.Store'; window.ReactDom = ReactDom; Actions.apiInit(); ReactDom.render( <AppCtrl />, document.getElementById('react') ); 

Note the action and api.store import. Here is api.store.

 import Reflux from 'reflux'; import Actions from '../actions/api.Actions'; import ApiFct from '../utils/sa.api'; let ApiStoreObject = { newData: { "React version": "0.14", "Project": "ReFluxSuperAgent", "currentDateTime": new Date().toLocaleString() }, listenables: Actions, apiInit() { ApiFct.setData(this.newData); }, apiInitDone() { ApiFct.getData(); }, apiSetData(data) { ApiFct.setData(data); } } const ApiStore = Reflux.createStore(ApiStoreObject); export default ApiStore; 

apiInit is doing a setData here but it could be the initial get calls. Here it the api.

 import request from 'superagent'; import apiActions from '../actions/api.Actions'; import saActions from '../actions/sa.Actions'; module.exports = { getData() { request.get('/routes/getData').end((err, res) => { this.gotData(res.body); }); }, gotData(data) { saActions.gotData1(data); saActions.gotData2(data); saActions.gotData3(data); }, setData(data) { request.post('/routes/setData').send(data).end((err, res) => { apiActions.apiInitDone(); }) }, }; 

Here we use actions to pass the data to the store. Here is the store.

 import Reflux from 'reflux'; import Actions from '../actions/sa.Actions'; import AddonStore from './Addon.Store'; import MixinStoreObject from './Mixin.Store'; function _GotData(data) { this.data1 = data; BasicStore.trigger('data1'); } let BasicStoreObject = { init() { this.listenTo(AddonStore, this.onAddonTrigger); }, data1: {}, listenables: Actions, mixins: [MixinStoreObject], onGotData1: _GotData, onAddonTrigger() { BasicStore.trigger('data2'); }, getData1() { return this.data1; }, getData2() { return AddonStore.data2; }, getData3() { return this.data3; } } const BasicStore = Reflux.createStore(BasicStoreObject); export default BasicStore; 

Note that reflux allows you to pass parameters in trigger. Here is the component.

 import React from 'react'; import BasicStore from '../stores/Basic.Store'; let AppCtrlSty = { height: '100%', padding: '0 10px 0 0' } const getState = () => { return { Data1: BasicStore.getData1(), Data2: BasicStore.getData2(), Data3: BasicStore.getData3() }; }; class AppCtrlRender extends React.Component { render() { let data1 = JSON.stringify(this.state.Data1, null, 2); let data2 = JSON.stringify(this.state.Data2, null, 2); let data3 = JSON.stringify(this.state.Data3, null, 2); return ( <div id='AppCtrlSty' style={AppCtrlSty}> React 0.14 ReFlux with SuperAgent<br/><br/> Data1: {data1}<br/><br/> Data2: {data2}<br/><br/> Data3: {data3}<br/><br/> </div> ); } } export default class AppCtrl extends AppCtrlRender { constructor() { super(); this.state = getState(); } componentDidMount = () => { this.unsubscribe = BasicStore.listen(this.storeDidChange); } componentWillUnmount = () => { this.unsubscribe(); } storeDidChange = (id) => { switch (id) { case 'data1': this.setState({Data1: BasicStore.getData1()}); break; case 'data2': this.setState({Data2: BasicStore.getData2()}); break; case 'data3': this.setState({Data3: BasicStore.getData3()}); break; default: this.setState(getState()); } } } 

We are not using mixins. React classes are deprecating mixins so now is a good time to do without. It may seen like overkill to use an extra store and api util but good practices are always good practice.

Map allows passing this with an optional parameter map(function(item){}, this);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM