Giving a state to parent component in React - javascript

I know you can pass down states and props in React from a parent component to a child component, but is there any way to do this the opposite way?
For example:
Given some child component:
var Child = React.createClass({
getInitialState: function(){
return {
data: ''
};
},
componentDidMount: function(){
this.setState({data: 'something'});
},
render: function() {
return (
<div>
...
</div>
);
}
});
and given some parent component:
var Parent = React.createClass({
render: function() {
return (
<div>
<Child />
...
</div>
);
}
});
Is there any way for me to give Parent the value of the state data from Child?

No.
But yes. But really no.
You cannot "pass" anything from a child to a parent in React. However, there are two solutions you can use to simulate such a passing.
1) pass a callback from the parent to the child
var Parent = React.createClass({
getInitialState: function() {
return {
names: []
};
},
addName: function(name) {
this.setState({
names: this.state.names.push(name)
});
},
render: function() {
return (
<Child
addName={this.addName}
/>
);
}
});
var Child = React.createClass({
props: {
addName: React.PropTypes.func.isRequired
},
handleAddName: function(event) {
// This is a mock
event.preventDefault();
var name = event.target.value;
this.props.addName(name);
},
render: function() {
return (
...
onClick={this.handleAddName}
...
);
}
});
The second option is to have a top-level state by using a Flux-style action/store system, such as Reflux or Redux. These basically do the same thing as the above, but are more abstract and make doing so on much larger applications very easy.

One way to do this is through a 'render props' pattern I was recently introduced to. Remember, this.props.children is really just a way for React to pass things down to a component.
For your example:
var Parent = React.createClass({
render: function() {
return (
<div>
<Child>
{(childState) => {
// render other 'grandchildren' here
}}
</Child>
</div>
);
}
});
And then in <Child> render method:
var Child = React.createClass({
propTypes: {
children: React.PropTypes.func.isRequired
},
// etc
render () {
return this.props.children(this.state);
}
});
This is probably best suited for cases where the <Child /> is responsible for doing something but doesn't really care much at all about the children that would be rendered in its place. The example the react training guys used was for a component that would fetch from Github APIs, but allow the parent to really control what / if anything was rendered with those results.

Related

Communicate between parent and child component in React.js

Just meet a problem about communication between parent and child component in React.
Child
var Child = React.createClass({
getInitialState() {
return {
childState: this.props.state,
};
},
changeState(e) {
this.setState({e.target.id});
},
render: function () {
return (
<button id='1' onClick={this.changeState}>1</button>
<button id='2' onClick={this.changeState}>2</button>
);
},
});
Parent
var Parent = React.createClass({
getInitialState() {
return {
parentState: '1',
};
},
render: function () {
return (
<Child state=this.state.parentState />
);
},
});
So right now Parent will pass the initial state '1' to child, I want the child component can change both child and parent's state. For example, when click the second button, both child and parent state are set to '2'. How can I achieve this? Thank guys!
to achieve this behaviour you need to communicate with your parent component through props.
var Child = React.createClass({
render: function () {
return (
<button id='1' onClick={this.props.changeState}>1</button>
<button id='2' onClick={this.props.changeState}>2</button>
);
},
});
var Parent = React.createClass({
getInitialState() {
return {
parentState: '1',
};
},
changeState: function(){
this.setState({parentState: e.target.id});
},
render: function () {
return (
<Child changeState={this.changeState} state={this.state.parentState} />
);
},
});
The idea behind this is, that you are passing down the changeState function from your parent Component to your child Component as a function and make it accessible through props. That way when you call the prop function in your child Component - the function in the parent Component will get executed.
That way you also don't need to keep a second "separate" state in your child component because the state of the parent component will be available in both and have the same value in both.
Pardon me for any typing mistakes - I am at the office but since you had no answer yet I wanted to help.

React how to update another component's state?

I am pretty new to react, trying to make some components work. I have
ObjectA:React.createClass({
propTypes: {
...
},
getInitialState: function() {
return {
myState: null
}
},
updateMyState: function(value) {
this.setState({
myState: value
})
}
render: function() {
return (<div className="my-class">'hello' +{this.state.myState}</div>);
}
});
ObjectB:React.createClass({
propTypes: {
...
},
render: function() {
return (<div className="my-class"><ObjectA / >
</div>);
}
});
I'd like to update ObjectA's state from ObjectB. How could I in ObjectB call ObjectA's updateMyState method?
Thanks!
The basic idea of React is the unidirectional data flow. That means that data is only shared downwards from an ancestor component to its children via the child's properties. We leave Flux like architectures and event emitters out of the equation for this simple example as it's not necessary at all.
The ONLY way then to change a component's state from the outside is changing the props it receives in the parent's render method. so myState would actually live in ObjectB and be given to ObjectA as property. In your example that would look like this:
ObjectA:React.createClass({
propTypes: {
...
},
render: function() {
return (<div className="my-class">'hello' +{ this.props.name }</div>);
}
});
ObjectB:React.createClass({
propTypes: {
...
},
getInitialState: function() {
return {
name: null
}
},
onNameChange: function(newName) {
this.setState({ name: newName })
},
render: function() {
return (
<div className="my-class">
<ObjectA name={ this.state.name } />
</div>
);
}
});

Can I set the children props in a react component from its parent?

Is it possible in react to do something like this:
var App = React.createClass({
getInitialState: function(){
return {
children: [<div/>, <div/>],
comp: ''
};
},
componentWillMount: function(){
this.setState({
comp: <SomeComponent children={this.state.children}/>
});
},
render: function(){
return (
<div>
{this.state.comp === '' ? this.state.children : this.state.comp}
</div>
);
}
});
When I try to do something similar to this I get: Uncaught TypeError: Cannot read property '_currentElement' of null so I assume the answer is no, but I figure there has to be some way considering
<SomeComponent>
<div/>
</SomeComponet>
where <SomeCompent/> renders a <div/>, is valid. However in this particular case I get the same error doing that, but if you look at something like React-Bootstrap https://react-bootstrap.github.io/, it has to be possible.
Why do you want to pass html tags as state? I'm not saying is bad, but there's always a better solution to what you asking. Let's say a child component passing a state Boolean true or false to parent component( Flux - Action by child component pass to store, parent components can pick up the state with onChange event). With that you could invoke the parent element to render different view.
_onChange: function() {
this.setState(this._getStateFromStores());
},
render: function() {
var view;
if (this.state.childAction) {
view = <SomethingTrue/> ;
}
if (!this.state.childAction) {
view = <SomethingFalse/> ;
}
return (
<div>
{view}
</div>
);

Call a 'Fathers' function form a 'Children' component in ReactJS

I have the next component 'Father' that contains a 'Children' component in React js.
var Father = React.createClass({
render: function () {
return (
<div>
<Children/>
</div>
);
},
onUpdate: function(state) {
this.setState(state);
} });
I want to call the onUpdate function on the father from the children BUT without calling the 'Children' method 'componentDidUpdate' because I'm using that method for some other thing that breaks my application.
How can I do that?
Pass it down in properties. If you need to update only specific parts and prevent your children from updating, use the method shouldComponentUpdate
var Father = React.createClass({
render: function () {
return (
<div>
<Children onUpdateCallback={this.onUpdate}/>
</div>
);
},
onUpdate: function(state) {
this.setState(state);
}
});
var Child = React.createClass({
render: function () { ... },
shouldComponentUpdate: function (prevProps, prevState) { ... return false}
});
If your Children can't/shouldn't update while the Parent does, I think you are probably doing something wrong, but good luck.

Passing AJAX Results As Props to Child Component

I'm trying to create a blog in React. In my main ReactBlog Component, I'm doing an AJAX call to a node server to return an array of posts. I want to pass this post data to different components as props.
In particular, I have a component called PostViewer that will show post information. I want it to by default show the post passed in from its parent via props, and otherwise show data that is set via a state call.
Currently, the relevant parts of my code looks like this.
var ReactBlog = React.createClass({
getInitialState: function() {
return {
posts: []
};
},
componentDidMount: function() {
$.get(this.props.url, function(data) {
if (this.isMounted()) {
this.setState({
posts: data
});
}
}.bind(this));
},
render: function() {
var latestPost = this.state.posts[0];
return (
<div className="layout">
<div className="layout layout-sidebar">
<PostList posts={this.state.posts}/>
</div>
<div className="layout layout-content">
<PostViewer post={latestPost}/>
</div>
</div>
)
}
});
and the child component:
var PostViewer = React.createClass({
getInitialState: function() {
return {
post: this.props.post
}
},
render: function() {
/* handle check for initial load which doesn't include prop data yet */
if (this.state.post) {
return (
<div>
{this.state.post.title}
</div>
)
}
return (
<div/>
)
}
});
The above works if I swap out the if statement and content in my child's render to this.props.* However, this would mean that I couldn't change the content later via state, correct?
TLDR: I want to set a default post to be viewed via props in a child component (results of an AJAX call), and I want to be able to change what post is being viewed by adding onClick events (of another component) that will update the state.
Is this the correct way to go about it?
Current hierarchy of my app's components are:
React Blog
- Post List
- Post Snippet (click will callback on React Blog and update Post Viewer)
- Post Viewer (default post passed in via props)
Thanks!
EDIT:
So what I ended up doing was attaching the props in ReactBlog using a value based on this.state. This ensured that it updates when I change state and renders correctly in child components. However, to do this I had to chain onClick callbacks up through all the various child components. Is this correct? It seems like it could get VERY messy. Here's my full example code:
var ReactBlog = React.createClass({
getInitialState: function() {
return {
posts: [],
};
},
componentDidMount: function() {
$.get(this.props.url, function(data) {
if (this.isMounted()) {
this.setState({
posts: data,
post: data[0]
});
}
}.bind(this));
},
focusPost: function(slug) {
$.get('/api/posts/' + slug, function(data) {
this.setState({
post: data
})
}.bind(this));
},
render: function() {
return (
<div className="layout">
<div className="layout layout-sidebar">
<PostList handleTitleClick={this.focusPost} posts={this.state.posts}/>
</div>
<div className="layout layout-content">
<PostViewer post={this.state.post}/>
</div>
</div>
)
}
});
var PostList = React.createClass({
handleTitleClick: function(slug) {
this.props.handleTitleClick(slug);
},
render: function() {
var posts = this.props.posts;
var postSnippets = posts.map(function(post, i) {
return <PostSnippet data={post} key={i} handleTitleClick={this.handleTitleClick}/>;
}, this);
return (
<div className="posts-list">
<ul>
{postSnippets}
</ul>
</div>
)
}
});
var PostSnippet = React.createClass({
handleTitleClick: function(slug) {
this.props.handleTitleClick(slug);
},
render: function() {
var post = this.props.data;
return (
<li>
<h1 onClick={this.handleTitleClick.bind(this, post.slug)}>{post.title}</h1>
</li>
)
}
});
var PostViewer = React.createClass({
getInitialState: function() {
return {
post: this.props.post
}
},
render: function() {
/* handle check for initial load which doesn't include prop data yet */
if (this.props.post) {
return (
<div>
{this.props.post.title}
</div>
)
}
return (
<div/>
)
}
});
Still hoping to get some feedback / hope this helps!
This is an old question, but I believe still relevant, so I'm going to throw in my 2 cents.
Ideally, you want to separate out any ajax calls into an actions file instead of doing it right inside a component. Without going into using something like Redux to help you manage your state (which, at this point in time, I would recommend redux + react-redux), you could use something called "container components" to do all of the heavy state lifting for you and then use props in the component that's doing the main layout. Here's an example:
// childComponent.js
import React from 'react';
import axios from 'axios'; // ajax stuff similar to jquery but with promises
const ChildComponent = React.createClass({
render: function() {
<ul className="posts">
{this.props.posts.map(function(post){
return (
<li>
<h3>{post.title}</h3>
<p>{post.content}</p>
</li>
)
})}
</ul>
}
})
const ChildComponentContainer = React.createClass({
getInitialState: function() {
return {
posts: []
}
},
componentWillMount: function() {
axios.get(this.props.url, function(resp) {
this.setState({
posts: resp.data
});
}.bind(this));
},
render: function() {
return (
<ChildComponent posts={this.state.posts} />
)
}
})
export default ChildComponentContainer;
A blog is static for the most part, so you could exploit React immutable structures to "render everything" all the time instead of using the state.
One option for this is to use a router (like page.js) to fetch data.
Here is some code http://jsbin.com/qesimopugo/1/edit?html,js,output
If you don't understand something just let me know ;)
Get rid of isMounted and make use of context if you're passing callbacks down several levels

Categories

Resources