-I have a Component (let's call it StorageComponent) that get a data(JSON) from a restful api (the request is made in componentDidMount)
-StorageComponent then passes the data to a child component and it will do stuff with other components to display the data and interact with user.
-Now there is another hierarchy of components independent of the above.
-This handle some form inputs by users, there is one component for each form input (radio button, checkbox, text, etc). And because every re-render will get rid of any state of the child, I had to use an object literal (call it ModelObject) to store each form input. So whenever a user enter something in a form it will make a call to Modelobject and store it there, the component will also ask for data from ModelObject.
-After The user entered all input he will eventually hit a submit button component in this hierarchy, where it will make a call to ModelObject to do ajax POST to the RestAPI. My problem is here, I would like for ModelComponent to get the data from the RestAPI again, so user will see the updated data. I thought forceUpdate() would work, I thought it would re-trigger rendering and thus componentDidMount in StorageComponent.
So what is the best way to do this. Moreover is there any bad practice mentioned above? Is this enough information?
edit:
the storageComponent hierarchy
var StorageComponent= React.createClass({
getInitialState: function(){
return {
data: []
};
},
componentDidMount: function(){
this.serverRequest = $.get(this.props.source, function(result){
result = JSON.parse(result);
this.setState({
data: result
});
}.bind(this));
},
render: function() {
return (
<div>
<Nav dataList={this.state.data} /> //whole bunch of other child component below this one
</div>
);
}
});
app.storageComponent= React.render(
<HabitModel source = "/api/listing/user"/>,
document.getElementById('myDiv')
);
the ModelObject that I've mentioned:
var formModel = {
newInfo: {
inputBox: "",
frequency: "",
date: "",
days: []
},
addDescription: function(description){
this.newHabitInfo.description = description;
},
addFrequency: function(selection){
this.newHabitInfo.frequency = selection;
},
addDay: function(startDay){
this.newHabitInfo.startDay = startDay;
},
getFrequency: function(){
return this.newHabitInfo.frequency;
},
//this is the function I want the second hierarchy of components to
//use to force the storageComponent to do the re-trigger the ajax
updateHabitListing: function(){
if(this.validate()){
app.habitListing.forceUpdate();
}else{
console.log("form not finish");
}
}
Related
I would like to have a component, which get the property from parent component and make an AJAX request, based on this propery. The parent component can change this property and my child component must get another one AJAX request.
Here is my code, but I am not sure it is optimal and even correct:
<News source={this.state.currentSource} />
Component:
var News = React.createClass({
propTypes: {
source: React.PropTypes.string
},
getInitialState: function() {
return {
entities: []
};
},
componentWillReceiveProps(nextProps) {
var url = 'http://localhost:3000/api/sources/' + nextProps.source + '/news';
this.serverRequest = $.get(url, function(result) {
this.setState({
entities: result
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
// ...
}});
module.exports = News;
Does componentWillReceiveProps is a good place for this code?
componentWillReceiveProps will work just fine, one thing I would do differently is if <News/> is getting its props from a parent component then a good pattern to follow is to have the parent component actually handle the api calls and pass down the results as props. This way your News component really only has to deal with rendering content based on its props vs rendering and managing state.
I can only see limited portion of your App so that might not fit your use case but here is a great article on doing that type of differentiation between smart and dumb components.
http://jaketrent.com/post/smart-dumb-components-react/
I'm using React with webpack and babel for compiling jsx. I have a parent component that looks like this:
const Menu = module.exports = React.createClass({
loadUser() {
let xhr = new XMLHttpRequest();
xhr.open('GET', this.state.url, true);
xhr.onload = function() {
let data = JSON.parse(xhr.responseText);
this.setState({
user: data
});
}.bind(this);
xhr.send();
},
componentDidMount() {
this.loadUser();
},
getInitialState() {
return {
url: this.props.url,
user: []
};
},
render: function() {
return (
<div className="menu">
<Nav user={this.state.user} />
...
</div>
);
}
});
As you can see, I attempt to use this.setState(...) to set this.state.user to the data received from the XMLHttpRequest. However, when I try to access it in the child, Nav, by simply calling, console.log(this.props.user), only an empty array, i.e. [], is printed.
What am I doing wrong here? I've read the React docs and I'm stumped. In the following tutorial, data is fetched from the server and transferred to the child component in a manner similar to what I've done (above). Any help would be greatly appreciated. If I need to supply any additional code or context, let me know. Thanks.
getInitialState is used at the first renderso it's normal it's complete before your ajax call since the ajax call is performed in ComponentDidMount which is triggered just after the first rendering.
Before the ajax call is empty your state.user will be empty, then when the data are received it should update your view with the new data.
In my opinion you're not doing anything wrong it depends on what you want to do.
For example you could put a message in getinitialstate like mgs:"Please wait data are fetching" and remove this msg when your data arrive.
Otherwise if you absolutely need your data to be ready before rendering your component you can use that : https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html Read carefully it may not fit your use.
Talking for myself I would put a loading msg in getinitialstate and proceed the way you do.
I have a store (MovieStore) which I would like to use as a central store to movies in my React App. I have a search page, that lists the movies based on a search string (and uses of course the MovieStore).
In addition I have a typeahead component, that makes suggestions based on the string the user typed into the search box. I want to use the same MovieStore for the typeahead too.
The problem is, that in this case the MovieStore should represent two states at the same time (the result list from the previous search, and the typeahead suggestions based on the current search string)
I ended up emitting two different kinds of events from the store, the typeahead listening to one event, the result list listening to the other, but it feels awkward. (because at the end of the day, one of the two components get out of synch, it just does not listen to a specific kind of event deliberately).
What better solution would you suggest for this?
Here is the simplified version of my code (I'm sure it is a bad design, because the store is not pure anymore, it was adjusted to these specific components)
var MovieStore = _.extend({}, EventEmitter.prototype, {
getAll: function() {
return movies;
},
//this will notify when the "search result" changed
emitListChange: function() {
this.emit('listChange');
},
//this will notify when the "typeahead string" changed
emitSearchChange: function() {
this.emit('searchChange');
},
// ... add/remove listeners
});
var Typeahead = React.createClass({
componentDidMount: function() {
MovieStore.addSearchChangeListener(this.onMoviesChanged);
},
onMoviesChanged() {
this.setState({
movies: Movies.getAll(),
});
},
//...
});
var List = React.createClass({
componentDidMount: function() {
MovieStore.addListChangeListener(this.onMoviesChanged);
},
onMoviesChanged() {
this.setState({
movies: Movies.getAll(),
});
},
//...
});
Why not have each component simply listen for a CHANGE event from the store. Then each component can query the store for the information it needs.
Example...
var Typeahead = React.createClass({
// deliberately omitting some stuff
componentDidMount() {
MovieStore.addChangeListener(this.onMovieStoreChange);
},
onMovieStoreChange() {
this.setState({movies: MovieStore.getAll()});
},
});
// Then in List
var List = React.createClass({
componentDidMount() {
MovieStore.addChangeListener(this.onMovieStoreChanged)
},
});
So, now, whenever the MovieStore updates, both the Typeahead and the List will update. Don't worry about the "cost" of updating both components when perhaps only one needs to be updated: it's miniscule.
Hope that helps
I'm building a React+Reflux application that, among other things, allows for the creation/deletion/editing of categories. So far I'm able to display all categories and handle creation/deletion of categories within a React component through associated stores and actions. This all works great, updates the database and re-renders the component as expected. My sticking point is when trying to drill down into a specific existing category in order to edit it.
I think I somehow need to pass a category Id to a store which will then pass it along to a php/sql query via an ajax call in order to get/set data specific to that particular category. If I bypass the store altogether and put the ajax call within the component itself I'm able to get it working via a url parameter with React-router (no auto re-render of course) but I haven't been able to figure out how to accomplish this through a store.
In other words, this more or less works:
"ManageCategories" React component that uses CategoryStore to list all categories each wrapped in an anchor tag that passes the category Id along to "ManageCategory" route/component
The "ManageCategory" component uses the category Id param directly in an ajax call within its getInitialState method to display data specific to the category
However, I think below is the more correct Reflux way to do this but I'm not sure how to get it to work:
"ManageCategories" component same as above
"ManageCategory" component that somehow passes its category Id param to the CategoryStore (or maybe a different "IndividualCategoryStore"?) which returns only data specific to that category and handles updates/edits to that category
I was able to get a sort of clunky version of this working by adding a new method ("getCategoryData") to the CategoryStore that is called in the getInitialState method of the "ManageCategory" component and is passed the categoryId param. This results in a flash of all categories (from the CategoryStore's getDefaultData) followed by the correct single category listing (from the component's getInitialState).
I feel fairly comfortable with the concepts behind React+Reflux but at this point I think it's likely I'm misunderstanding something fundamental. Worked on this particular issue for more than a week but none of the examples/tutorials/docs I've found address the specific question of passing a variable to a data store.
Actions:
var Actions = Reflux.createActions([
"createCategory",
"deleteCategory",
"editCategory"
]);
CategoryStore:
var CategoryStore = Reflux.createStore({
listenables: [Actions],
onCreateCategory: function(catName) {
// ajax call to create new category that calls updateCategories on success
},
onDeleteCategory: function(catId) {
// ajax call to delete category that calls updateCategories on success
},
updateCategories: function(){
$.ajax({
url: url + '?action=getAllCategories',
async: false,
dataType: 'json',
success: function(categoryData) {
this.categories = categoryData;
}.bind(this),
error: function(xhr, status, err) {
console.error(url, status, err.toString());
}.bind(this)
});
this.trigger(this.categories);
},
getDefaultData: function() {
$.ajax({
url: url + '?action=getAllCategories',
async: false,
dataType: 'json',
success: function(categoryData) {
this.categories = categoryData;
}.bind(this),
error: function(xhr, status, err) {
console.error(url, status, err.toString());
}.bind(this)
});
return this.categories;
}
});
Category Component:
var Category = React.createClass({
handleDeleteCategory: function() {
Actions.deleteCategory(this.props.id);
},
render: function() {
return (
<li className="category">
<IconButton icon="action-highlight-remove" onClick={this.handleDeleteCategory} />
<h5><a href={"/#/manage-category/" + this.props.id}>{this.props.name} ({this.props.id})</a></h5>
</li>
);
}
});
ManageCategories Component:
var ManageCategories = React.createClass({
mixins: [
Reflux.connect(CategoryStore, "categories")
],
getInitialState: function() {
return {
categories: []
};
},
handleCreateCategory: function() {
// category creation code
},
render: function() {
var categoryNodes = this.state.categories.map(function(category) {
return (
<Category name={category.name} id={category.id} />
)
});
return (
<div className="dev-tools-container">
<h1>Developer Tools</h1>
<div className="categories">
<h3>Categories</h3>
<ul>
{categoryNodes}
</ul>
<h4>Create New Category:</h4>
<form>
<label htmlFor="new-category-name">Category Name</label> <input type="text" id="new-category-name" /><br />
<PaperButton label="Create" primary={true} onClick={this.handleCreateCategory} />
</form>
</div>
</div>
);
}
});
Thanks in advance for any insights or assistance.
After finally posting my question here I think I may have figured out where I was going astray all along. I was thinking in terms of passing the category Id to the store to filter the data therein when all I really need to do is take the full collection of data from the store and consume it selectively within the component.
So once routed to the ManageCategory component with the catId passed along as a url param, all I need to do is filter the data based on the catId.
For example, once in the ManageCategory component I can use lodash to filter and pluck the name value of the current category, as below. No need to edit the collection of data stored in the CategoryStore at all.
var ManageCategory = React.createClass({
mixins: [
Reflux.connect(CategoryStore, "categoryData")
],
getInitialState: function() {
return {
categoryData: []
};
},
render: function() {
var categoryName = _.chain(this.state.categoryData)
.filter({"id": this.props.params.catid})
.pluck("name");
return (
<div className="category-container">
<h1>{categoryName}</h1>
</div>
);
}
});
Feel free to let me know if there's a better way of doing this but for now this is exactly what I needed. Hopefully something from all of this will be helpful to someone else too.
This may be treading that line between answerable and opinionated, but I'm going back and forth as to how to structure a ReactJS component as complexity grows and could use some direction.
Coming from AngularJS, I want to pass my model into the component as a property and have the component modify the model directly. Or should I be splitting the model up into various state properties and compiling it back together when sending back upstream? What is the ReactJS way?
Take the example of a blog post editor. Trying to modify the model directly ends up looking like:
var PostEditor = React.createClass({
updateText: function(e) {
var text = e.target.value;
this.props.post.text = text;
this.forceUpdate();
},
render: function() {
return (
<input value={this.props.post.text} onChange={this.updateText}/>
<button onClick={this.props.post.save}/>Save</button>
);
}
});
Which seems wrong.
Is it more the React way to make our text model property state, and compile it back into the model before saving like:
var PostEditor = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: this.props.post.text
});
},
updateText: function(e) {
this.setState({
text: e.target.value
});
},
savePost: function() {
this.props.post.text = this.state.text;
this.props.post.save();
},
render: function() {
return (
<input value={this.state.text} onChange={this.updateText}/>
<button onClick={this.savePost}/>Save</button>
);
}
});
This doesn't require a call to this.forceUpdate(), but as the model grows, (a post may have an author, subject, tags, comments, ratings, etc...) the component starts getting really complicated.
Is the first method with ReactLink the way to go?
Updating 2016:
React is changed, and explanation "props vs state" became very simple. If a component needs to change data - put it in a state, otherwise in props. Because props are read-only now.
What's the exact difference between props and state?
You can find good explanation here (full version)
Your second approach is more like it. React doesn't care about models so much as it cares about values and how they flow through your app. Ideally, your post model would be stored in a single component at the root. You then create child components that each consume parts of the model.
You can pass callbacks down to the children that need to modify your data, and call them from the child component.
Modifying this.props or this.state directly is not a good idea, because React will not be able to pick up on the changes. That's because React does a shallow comparison of your post prop to determine if it has changed.
I made this jsfiddle to show how data could flow from an outer to an inner component.
The handleClick method shows 3 ways to (im)properly update state:
var Outer = React.createClass({
getInitialState: function() {
return {data: {value: 'at first, it works'}};
},
handleClick: function () {
// 1. This doesn't work, render is not triggered.
// Never set state directly because the updated values
// can still be read, which can lead to unexpected behavior.
this.state.data.value = 'but React will never know!';
// 2. This works, because we use setState
var newData = {value: 'it works 2'};
this.setState({data: newData});
// 3. Alternatively you can use React's immutability helpers
// to update more complex models.
// Read more: http://facebook.github.io/react/docs/update.html
var newState = React.addons.update(this.state, {
data: {value: {$set: 'it works'}}
});
this.setState(newState);
},
render: function() {
return <Inner data={this.state.data} handleClick={this.handleClick} />;
}
});
From React doc
props are immutable: they are passed from the parent and are "owned" by the parent. To implement interactions, we introduce mutable state to the component. this.state is private to the component and can be changed by calling this.setState(). When the state is updated, the component re-renders itself.
From TrySpace: when props (or state) are updated (via setProps/setState or parent) the component re-renders as well.
A reading from Thinking in React:
Let's go through each one and figure out which one is state. Simply
ask three questions about each piece of data:
Is it passed in from a parent via props? If so, it probably isn't
state.
Does it change over time? If not, it probably isn't state.
Can you compute it based on any other state or props in your
component? If so, it's not state.
I'm not sure if I'm answering your question, but I've found that, especially in a large/growing application, the Container/Component pattern works incredibly well.
Essentially you have two React components:
a "pure" display component, which deals with styling and DOM interaction;
a container component, which deals with accessing/saving external data, managing state, and rendering the display component.
Example
N.B. This example is a probably too simple to illustrate the benefits of this pattern, as it is quite verbose for such a straightforward case.
/**
* Container Component
*
* - Manages component state
* - Does plumbing of data fetching/saving
*/
var PostEditorContainer = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: getPostText()
});
},
updateText: function(text) {
this.setState({
text: text
});
},
savePost: function() {
savePostText(this.state.text);
},
render: function() {
return (
<PostEditor
text={this.state.text}
onChange={this.updateText.bind(this)}
onSave={this.savePost.bind(this)}
/>
);
}
});
/**
* Pure Display Component
*
* - Calculates styling based on passed properties
* - Often just a render method
* - Uses methods passed in from container to announce changes
*/
var PostEditor = React.createClass({
render: function() {
return (
<div>
<input type="text" value={this.props.text} onChange={this.props.onChange} />
<button type="button" onClick={this.props.onSave} />
</div>
);
}
});
Benefits
By keeping display logic and data/state management separate, you have a re-usable display component which:
can easily be iterated with different sets of props using something like react-component-playground
can be wrapped with a different container for different behavior (or combine with other components to build larger parts of your application
You also have a container component which deals with all external communication. This should make it easier to be flexible about the way you access your data if you make any serious changes later on*.
This pattern also makes writing and implementing unit tests a lot more straightforward.
Having iterated a large React app a few times, I've found that this pattern keeps things relatively painless, especially when you have larger components with calculated styles or complicated DOM interactions.
*Read up on the flux pattern, and take a look at Marty.js, which largely inspired this answer (and I have been using a lot lately) Redux (and react-redux), which implement this pattern extremely well.
Note for those reading this in 2018 or later:
React has evolved quite a bit since this answer was written, especially with the introduction of Hooks. However, the underlying state management logic from this example remains the same, and more importantly, the benefits that you get from keeping your state and presentation logic separate still apply in the same ways.
I think you're using an anti-pattern which Facebook has already explained at this link
Here's thing you're finding:
React.createClass({
getInitialState: function() {
return { value: { foo: 'bar' } };
},
onClick: function() {
var value = this.state.value;
value.foo += 'bar'; // ANTI-PATTERN!
this.setState({ value: value });
},
render: function() {
return (
<div>
<InnerComponent value={this.state.value} />
<a onClick={this.onClick}>Click me</a>
</div>
);
}
});
The first time the inner component gets rendered, it will have { foo: 'bar' } as the value prop. If the user clicks on the anchor, the parent component's state will get updated to { value: { foo: 'barbar' } }, triggering the re-rendering process of the inner component, which will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a reference to the same object, when the object gets mutated on line 2 of the onClick function, the prop the inner component had will change. So, when the re-rendering process starts, and shouldComponentUpdate gets invoked, this.props.value.foo will be equal to nextProps.value.foo, because in fact, this.props.value references the same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short circuit the re-rendering process, the UI won't get updated from 'bar' to 'barbar'