I would like to perform some initial data loading when my first route is rendered (for example, i want to load a list of news articles)
I made a component called News.js which renders the articles. The problem i'm experiencing with the FLUX model is where to load this initial data.
The service i have made to load the data is the following:
import request from 'superagent';
class NewsService {
fetchArticles(callback) {
request
.get('http://localhost/articles')
.accept('json')
.end(function(err, res){
console.log(err);
var result = JSON.parse(res.text);
callback(result);
})
}
}
export default new NewsService ();
This service has to be called somewhere. According to the ReactJS documentation i would perform this operation like this:
export default class News extends React.Component {
constructor() {
super();
this.state = {
_articles: []
}
}
componentDidMount() {
NewsService.fetchProjects(function(articles){
// load articles in the state
this.setState({_articles: _articles})
});
}
render() {
return (
<section>
<h1>Articles</h1>
<ul>
{this.state.articles.map((article) => {
<li>{article.title}</li>
})}
</ul>
</section>
)
}
}
Now my question is, isn't this against the flux principle? Shouldn't the data be called as an Action which then stores it's data in a store such as NewsStore?
Should an action be like the following:
var NewsActions = {
load: function() {
NewsService.fetchProjects(function(articles){
// store the articles in the NewsStore and dispatch afterwards
});
},
create: function(project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_CREATE,
project: project
});
},
update: function(id, project) {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_UPDATE,
id: id,
project: project
})
},
destroy: function() {
AppDispatcher.dispatch({
actionType: NewsConstants.ARTICLE_DESTROY,
id: id
})
}
};
export default NewsActions;
In the Chat-app example of reactjs they provide an API call example. However this API call is called on the application start up (in app.js) which is not applicable in my scenario as i would like to use routings. Would i load the data in the router then? (I am using react-router)
Any feedback regarding this matter or improvements of this code is more than welcome.
EDIT
isn't this against the flux principle?
Maybe, maybe not. Seems like Flux is pretty flexible. From what I understand, it's more of a framework of principles rather than a strict "protocol". It's hard to say, but it appears that both example you've given will work. Like you said, according to the docs, they recommend fetching the data in componentDidMount:
componentDidMount: function() {
$.get(this.props.source, function(result) {
// ...
However, in your example, you're simply moving that API call into a service, which can then interact with the store/dispatcher, etc., in order to be utilized across the entire application.
So what you've done is moved a nice chunk of your application logic to, essentially, its own module (well, a module that is a part of your dispatchers). It appears that it will meet your needs: it can be used across your app, and it can be pulled out or plugged back in as you see fit. I don't see anything wrong with it. Could it be against some principle of Flux? Maybe, maybe not. I doubt it matters, though.
ORIGINAL
I'm not well-versed in Flux architecture, but looking at one of Facebook's examples in their GitHub repo (specifically, Line 24 of TodoMVC):
function getTodoState() {
return {
allTodos: TodoStore.getAll(),
areAllComplete: TodoStore.areAllComplete()
};
}
Their example doesn't show how TodoStore interacts with the server, but it does look like for their initial state, they're simply querying the todos in the store, and then for changes, their listening for and emitting events.
So as far as getting the initial state, it looks like their example shows querying the store directly. Obviously since the time they made that example and now, there may have been changes, but it may be worth investigating some examples in the Flux repo to get an idea of how it was designed.
Related
So I'll get right to it :
I'm trying to remake an existing app that used Angular 1 , instead using VueJS 2.
Being unfamiliar to Angular1 I find it challenging to decide on a few things :
1.What are factories(probably services), where do I place/write manage them?
2.Okay I know angular is big on controllers, but I cannot seem to understand if I were to use Vue , what's the alternative to the controller, and where to hold the code.
So, basically what I've gotten so far is to use VueX for state management and I have moved some services there, however - I can't figure out for example if a certain service #requires 'NameOfOtherService' , does it mean it imports it like in NodeJS const NameOfOtherService = require ('insert/path/here'); ?
Basically the app gets data from an API and php scripts, for example :
In the angular 1 version in my appsettings, which is using an AppConfig module I have a pathFromScript(script) => {} // etc.
My question is , how do I manage EVERYTHING that's going on within one app like that translated to Vue?
Thank you in advance I know it's entirely a shot in the dark here.
Yours truly ,
As for Vue.Js, your angular controllers are methods. If you need to get some data from an API, you can use a method to call a Vuex action and either return the value back to the method or add it to your state, so it's available everywhere. This will look like the example below.
I guess your factories / services are managing API calls. I would place that inside Vuex with axios to make API calls.
Methods is the same as controllers in Angular. You create methods on the specific page. If you need a method to be resused multiple places, you can use mixins. Mixins extends the specific page by importing it. You can read about it here: https://v2.vuejs.org/v2/guide/mixins.html
page.vue
{{data}}
export default {
import { mapActions, mapGetters } from 'vuex'
...
computed: {
...mapGetters({
data: 'getData'
})
},
methods: {
...mapActions({
getServerData: 'getDataFromServer'
})
},
created () {
this.getServerData()
}
}
store.js (vuex)
import axios from 'axios'
state: {
data: null
},
getters: {
getData: state => {
return state.data
}
},
mutations: {
setDataFromServer: (state, payload) => {
state.data = payload
}
},
actions: {
getDataFromServer: ({ commit }) => {
return axios.get('https://website.com/api/data').then((response) => {
commit('setDataFromServer', response)
})
}
}
In this example the created is calling the vuex action to get the data from the server. When the server returns the value, it calls a mutation which sets the response in a state (data). The mapGetters which calls the getters in vuex, returns the data from state. It updates whenever the state changes.
Hope this makes sense
I am currently designing the architecture of a webapp using React.JS. In React, data flow is unidirectional. Sometimes (often) we want to communicate between views. React solves this problem with flux. So far so good, I now have stores that holds the (shared) data.
Now I wonder if flux is also the right solution for this old problem:
A view, say a table needs data from a server that it should render. I am not that experienced with React therefor pardon me if it's a stupid question, but is flux also the right solution for getting data from a server and parse it to the registered views? If so, is there a best practice to work with async data? If not, what would be an appropriate way to fetch data from the server for a react view?
I actually don't want a view to call for the data. In my opinion a view should be as stupid as possible...
..is flux also the right solution for getting data from a server and
parse it to the registered views?
You can do this with flux. Flux is al about Action Creators. In the flux architecture the idea is that you have a Controller View that registers itself with a Store. The Controller View is your smart component, it gets it state from the Store(s) and usually it renders child components, dumb components, where it passes (some of) the state as properties to it's child(ren).
When for instance you're fetching data from a server, you need to trigger an Action Creator, this Action Creator will then call a Web API utility that performs the actual request. When successful, the Web API utility will call a Server Action Creator that dispatches an action containing the payload received from the server. Any registered Store will then process this action and emit a change event. This way any Controller View interested in a Store, will be notified by it when the Store's data has changed. The Controller View will update it's state and re render itself and any child(ren) to display correct data.
This means that you can call an Action Creator to for instance fetch data from the server in the componentDidMount (note that this hook is only executed once though!) method of the Controller View.
Initially the Controller View will ask the store for data and get, for example, an empty array, which will be set as the Controller View's state and the Controller View will render something empty. Then after the data has been fetched, the Controller View (who is notified by the Store) will again retrieve the data from the Store, only now it will not be empty. The Controller View updates it's state accordingly, triggering a re render and displaying the appropriate data.
The essence of this is captured in this minimal (pseudo) code:
// Action Creator: actions/data.js
import { fetchData } from '../utils/server';
export function fetch() {
fetchData();
}
// Server Action Creator: actions/data-server.js
import dispatcher from '../dispatcher';
export function receiveData(data) {
dispatcher.dispatch({
type: 'RECEIVE_DATA',
payload: data
});
}
// Web API Utility: utils/server.js
import request from 'request';
import { receiveData } from '../actions/data-server';
export function fetchData() {
request.get('https://api.com/v1/data', receiveData);
}
// Store: stores/store.js
import dispatcher from '../dipatcher';
import { EventEmitter } from 'events';
const CHANGE_EVENT = 'change';
let __data__ = [];
const Store = Object.assign({}, EventEmitter.prototype, {
emitChange () {
this.emit(CHANGE_EVENT);
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
},
getData () {
return __data__;
}
});
Store.dispatchToken = dispatcher.register(function (action) {
switch (action.type) {
case 'RECEIVE_DATA':
__data__ = action.payload;
Store.emitChange();
break;
}
});
export default Store;
// Controller View: components/List.jsx
import React from 'react';
import Store from '../stores/Store');
import { fetch } from '../actions/data';
var __getState = function () {
return {
data: Store.getData()
};
};
export default React.createClass({
getInitialState () {
return __getState();
},
componentWillMount () {
fetch();
},
componentDidMount () {
Store.addChangeListener(this._onChange);
},
componentWillUnMount () {
Store.removeChangeListener(this._onChange);
},
_onChange () {
this.setState( __getState() );
},
render () {
const { data } = this.state;
return (
<div className="list">
<ul>
{ data.map(dataItem => <li>{ dataItem.name }</li> )}
</ul>
</div>
);
}
});
Check out more detailed flux examples here.
I actually don't want a view to call for the data. In my opinion a
view should be as stupid as possible...
It's good practice to distinguish between smart and dumb components. The idea is that smart components hold state and call actions, while the dumb components have no dependencies on the rest of the application. This creates a clear separation of concerns and allows for better reusability and testability of your components. Read more about it here.
Also there are some interesting alternatives besides flux. For instance there is redux. It uses a single immutable state Object (i.e. Store) where a reducer (pure function) is only allowed to modify application state by means of actions.
And definitely check out react-refetch if you're mostly fecthing and rendering read-only data from a server.
A common way to do this is to initialize the async call in the view code's componentDidMount() method, and render it properly.
The code might be different for different flux implementations but you get the idea
getInitialState() {
return {
rows: MyStore.getState().rows
}
}
componentDidMount() {
this.listenTo(MyStore, this.onChange)
MyActions.fetchData()
}
onChange() {
this.setState(this.getInitialState())
}
....
render() {
if (!this.state.rows) {
return <span />
}
return <Table rows={this.state.rows}>
}
As for the container components, it's totally up to you to use it or not.
We want to build a production-ready social network website (Facebook or Instagram style). We plan to use Node on the server side and are looking for the best technology for rendering view components on the server side.
SEO friendliness is a must, so Angular doesn’t seem like a good fit (unless anyone can advocate for Prerender.io as a solid choice).
We also wish to support AJAX so that most of the rendering would be done on the server, but updates would be done on the client.
Having looked at React, it seems like a good choice, but there’s one thing I’m worried about - the fact that out of the box, you would need to load all data at the route level as opposed to the component level (since renderToString is synchronous - see the discussion here
I don't really understand what would be a robust alternative for server side rendering if we're passing on React.
If I understnd correctly, the basic way (which does allow async loading from within sub components) would be something like:
// The main page route
app.get('/',function(){
var html = renderBase();
renderHeader(html)
.then(
renderFeed(html)
).then(
renderFooter(html)
);
})
renderBase = function(){
return = "<html><body>..."
}
renderHeader = function(){
someIoFunction(function(){
// build HTML based on data
})
}
Seemingly using a template engine such as Jade might relieve us of some of the burden, but I can't seem to find anything on this "React vs. Templating engine" so-called issue, so probably I'm not seeing it correctly.
Thanks.
Basically the solutions come down to using a convention where each component has a static function which returns the data it needs, and it calls the function of the same name on the components it needs. This is roughly how it looks:
class CommentList {
static getData = (props) => {
return {
comments: api.getComments({page: props.page})
};
}
}
class CommentApp {
static getData = (props) => {
return {
user: api.getUser(),
stuff: CommentList.getData({page: props.commentPage})
};
}
render(){
return <CommentList comments={this.props.stuff.comments} />
}
}
Or you can figure out all of the data requirements at the top of the tree, but while simpler to start out, it's more fragile. This is what you do with templates.
Let me explain the problem that I've faced recently.
I have React.js + Flux powered application:
There is a list view of articles (NOTE: there are multiple of of different lists in the app) and article details view inside it.
But there is only one API endpoint per each list which returns array of articles.
In order to display the details I need to find article by id in array. That works pretty fine. I trigger action which makes request to server and propagates store with data, when I go to details screen then I just get the necessary article from that array in store.
When user lands on article details view before list (stores are empty) then I need to make a request.
Flow looks like: User loads details view -> component did mount -> stores are empty -> rendered empty -> fetchArticles action is triggered -> request response is 200 -> stores now have list of articles -> component did update -> rendered with data successfully
Component could look as follows:
let DetailsComponent = React.createClass({
_getStateFromStores() {
let { articleId } = this.getParams();
return {
article: ArticleStore.getArticle(articleId)
};
},
componentDidMount() {
// fire only if user wasn't on the list before
// stores are empty
if (!this.state.article) {
ArticleActions.fetchArticles('listType');
}
},
render() {
return <ArticleDetails article={this.state.article} />;
}
});
The interesting part comes next:
Now I need to make another request to server but request options depend on the article details. That's why I need to make second request after the first one on the details view.
I've tried several approaches but all of them look ugly. I don't like calling actions from stores that makes stores too complicated. Calling action inside action in this case doesn't work well because I will need to find article from store inside that action.
Solution (?!)
What I've came up with is to use callback in action inside component and it feels much more cleaner:
let DetailsComponent = React.createClass({
_getStateFromStores() {
let { articleId } = this.getParams();
return {
article: ArticleStore.getArticle(articleId)
};
},
componentDidMount() {
if (!this.state.article) {
ArticleActions.fetchArticles('listType', () => {
this._requestAdditionalData();
});
}
this._requestAdditionalData();
},
_requestAdditionalData() {
if (this.state.article) {
ArticleActions.fetchAdditional(this.state.article.property);
}
},
render() {
return <ArticleDetails article={this.state.article} />;
}
});
What's your input?
Consider move the second call to get a detail article to the ArticleDetails component componentDidMount() life cycle method.
So if the article is not set, do not render the ArticleDetails component at all by return null / false.
I'm relatively new to ReactJS and have been seduced by the ease of implementing server-side rendering to reduce "time to first tweet". I'm running a Node-Express-React stack which pre-renders the markup on the server using React's renderComponentToString.
It works fine when the components can be rendered synchronously, however I'm struggling when it comes to implementing ajax-populated components (however this applies to any asynchronous operation during the initialization of the component, e.g. websocket).
Take the example from the React website: http://facebook.github.io/react/tips/initial-ajax.html
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
It won't work on the server, since componentDidMount is never triggered when using renderComponentToString.
This simple case can be worked around by using the same HTTP request wrapper on the client and on the server (instead of using jQuery's $.get), and by pre-fetching the data before instantiating the component and passing it as a prop.
However, in an actual, complex app, asynchronous dependencies can get very complicated and pre-fetching doesn't really fit the descendant approach of building React structures.
How can I implement an asynchronous initialization pattern in React that can be rendered on the server without actually mounting anything (i.e. no DOM emulation a la PhantomJS, which is the whole point of using renderComponentToString) ?
I believe the most practical way to do this is to make an optional prop for the preloaded data, like so:
getInitialState: function() {
if (this.props.initialLastGist) {
var lastGist = this.props.initialLastGist;
return {
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
};
} else {
return {};
}
},
componentDidMount: function() {
if (!this.props.initialLastGist) {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
},
With a setup like this, the component can be rendered immediately if the preloaded data is present; otherwise the AJAX request will be sent while mounting.
Currently, server rendering is always synchronous and componentDidMount isn't called on the server because it usually involves DOM manipulation. Sorry I don't have a better solution here right now, but in general you want to minimize the number of HTTP requests to the server so it's worth thinking through your architecture so that you can collect all the data you need on the server.
You could take a look at the react-quickstart project, which uses react-async and ships with a server-rendering example. react-async provides a decorated renderComponentToString method that pre-fetches asynchronous state and renders it into the initial markup. However, you will need to specify asynchronous state separately from 'regular' state.
you can check out react-nexus (https://github.com/elierotenberg/react-nexus) which helps you declare all your async dependencies ahead of time.
but I realize that react-nexus is your own answer to this problem so you probably already found it !