ReactJS - Passing prop value to component before final ReactDOM.render() - reactjs

I want to pass a value to a component this way, but when I try to console log this.props.vouch it returns an undefined value.
I know it will work if I put:
<Something onClick={this.log} vouch=this.props.vouch />
and
ReactDOM.render(<List vouch="value 1"/>, document.getElementById('react-app'))
But I will want to use different vouch value later in the code and be able to reuse Something component.
var Something = React.createClass({
propTypes:{
vouch: React.PropTypes.string,
},
render: function() {
return (
<div>
<h1 onClick={this.props.onClick} vouch={this.props.vouch}>Click!</h1>
</div>
);
}
});
var List = React.createClass({
log: function() {
console.log(this.props.vouch);
},
render: function () {
return (
<Something onClick={this.log} vouch="value 1" />
<Something onClick={this.log} vouch="value 2" />
);
}
});
ReactDOM.render(<List />, document.getElementById('react-app'));

You can't set this.props from child component, but you can pass data using data attributes, like this
<h1 onClick={this.props.onClick} data-vouch={this.props.vouch}>Click!</h1>
...
log: function (e) {
console.log(e.target.dataset.vouch);
},
Example
or using .bind, like this
<h1 onClick={this.props.onClick.bind(null, this.props.vouch)}>Click!</h1>
...
log: function (vouch) {
console.log(vouch);
},
Example
or call callback in child component and pass props, like this
handleClick: function () {
this.props.onClick(this.props.vouch)
},
render: function() {
return (<div>
<h1 onClick={this.handleClick}>Click!</h1>
</div>)
}
...
log: function (vouch) {
console.log(vouch);
},
Example

You're not passing this.props.vouch to List, so your log will return undefined.
var Something = React.createClass({
propTypes:{
vouch: React.PropTypes.string,
},
onClick: function() {
this.props.onClick( this.props.vouch )
},
render: function() {
return (
<div>
<h1 onClick={this.onClick.bind( this )} vouch={this.props.vouch}>Click!</h1>
</div>
);
}
});
var List = React.createClass({
log: function( vouch ) {
console.log( vouch );
},
render: function () {
return this.props.vouch.map( vouch => <Something onClick={ log } vouch = { vouch } /> )
}
});
var vouch = [
{
value: 'foo'
},
{
value: 'bar'
}
]
ReactDOM.render(<List vouch={ vouch } />, document.getElementById('react-app'));
The actual problem of your log not working could also be solved by passing List.log to Something (which you do already) and then invoking it in the context of Something by using <h1 onClick={ this.props.onClick.call( this ) and having log console.log( this.props.vouch ) but this solution would be nasty from a maintainability standpoint.
It is important to understand the parent->child relationship between components that you are creating. At any point you can grab your vouch data and inject it but by injecting it at the List component you keep all children pure i.e. when you render you are passing the state of the system, you arent attempting to grab state or worse, mutate, state during the life-cycle of a render.

Related

Unable to set state in the component

When the click event takes place, the state is not set to the value given inside the CompOne. It still show the initial state and console logs the old state which is "hello".
var CompOne = React.createClass({
getInitialState: function() {
return {
edit: "hello"
}
},
editme: function () {
this.setState({
edit: "there"
})
console.log(this.state.edit)
},
render: function(){
return (
<div>
{this.props.name}
<button onClick={this.editme}>Edit</button>
</div>
)
}
})
var Pri = React.createClass({
render: function () {
return (
<div>
< CompOne name = "Anne"/>
< CompOne name = "Bob"/>
</div>
);
}
})
ReactDOM.render( <Pri /> , document.getElementById("root"));
Function setState is not synchronous. Here is a note about this from React documentation;
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls
may be batched for performance gains.
In human terms this means that if you call setState and try to read the state immediately, the state could be changed or it could be the same.
The solution that you can use is to pass a callback to setState method as a second parameter:
editme: function () {
this.setState({
edit: "there"
}, function(){
// this function would be invoked only when the state is changed
console.log(this.state.edit);
});
}
The purpose of the second parameter is described in the same documentation article:
The second parameter is an optional callback function that will be
executed once setState is completed and the component is
re-rendered.
You need to use the callback function in setState because setState takes time to mutate and you console.log gets executed before the state is mutated as statements are executed asynchronously.
editme: function () {
this.setState({
edit: "there"
}, function(){
console.log(this.state.edit)
})
},
var CompOne = React.createClass({
getInitialState: function() {
return {
edit: "hello"
}
},
editme: function () {
this.setState({
edit: "there"
}, function(){
console.log(this.state.edit)
})
},
render: function(){
return (
<div>
{this.props.name}
<button onClick={this.editme}>Edit</button>
</div>
)
}
})
var Pri = React.createClass({
render: function () {
return (
<div>
< CompOne name = "Anne"/>
< CompOne name = "Bob"/>
</div>
);
}
})
ReactDOM.render( <Pri /> , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="root"></div>

Sending state to parent components with React.js

I have a React.js app that is constructed like the following:
// App component - represents the whole app
App = React.createClass({
render() {
return (
<div>
<Landing />
<Description />
<Skills/>
</div>
);
}
});
where "Landing", "Description" and "Skills" are all children components of the App component.
In Landing, I have a child component called Social Menu, that gets called using:
<SocialMenu items={ ['Home', 'Services', 'About', 'Contact us']} />
It looks like this:
SocialMenu = React.createClass({
getInitialState: function(){
return { focused: 0 };
},
componentDidMount: function() {
MichaelReactStore.addChangeListener(this.state.focused);
},
clicked: function(index){
// The click handler will update the state with
// the index of the focused menu entry
this.setState({focused: index});
},
render: function() {
// Here we will read the items property, which was passed
// as an attribute when the component was created
var self = this;
// The map method will loop over the array of menu entries,
// and will return a new array with <li> elements.
return (
<div>
<ul className="testblocks">{ this.props.items.map(function(m, index){
var style = '';
if(self.state.focused == index){
style = 'focused';
}
// Notice the use of the bind() method. It makes the
// index available to the clicked function:
return <li key={index} className={style} onClick={self.clicked.bind(self, index)}>{m}</li>;
}) }
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
<ItemDetails item={ this.props.items[this.state.focused] } />
</div>
);
}
});
ItemDetails = React.createClass({
render: function() {
return (
<div>{this.props.item}</div>
);
}
});
What I would like to do is "send the state up" to the App component from the social menu. Then I would like to send that data down as a prop to the Skills component, where it will show some dynamic data depending on that state.
How would I do this? Thank you!
(I know this isn't sustainable for a larger app, but for this app, I just need a simple solution)
I would manage the state in the root component and make focused a property (this.props.focused) in all the components you need it. Where you now do the setState you call an callback, like so:
this.props.onFocusChanged(index)
You give this callback as a property to the Landing, and in the Landing you give it as a property to the SocialMenu. Your App would look something like this:
App = React.createClass({
getInitialState: function(){
return { focused: 0 };
},
clicked: (index) => {
this.setState({focused: index});
},
render() {
return (
<div>
<Landing onFocusChanged={this.clicked} focused={this.state.focused} />
<Description />
<Skills focused={this.state.focused}/>
</div>
);
}
});

React: Access child element with findDOMNode throws error

I have a Container component, it gets a few children.
on componentDidUpdate i would like to change a style attribute.
seems like the Container should be able to handle this, instead of each child handling itself.
Problem, react throws an error when trying to get Dom node of child.
var childA = this.props.children[0];
React.findDOMNode(childA);
=> Uncaught Error: Invariant Violation: Element appears to be neither ReactComponent nor DOMNode (keys: )
EDIT: That ^^^ was the wrong way to write react!
If your children need to do something, you should always try to pass it down from the top.
lets say you have 3 colors: green,red,blue and you want your children to be colored that way, but maybe they change order often. pass it down
Parent = React.createClass({
renderChildren: function(){
var colors = ["red", "blue", "green"]
return React.Children.map(this.props,children, function(child, index){
// this returns a legit clone, adding one extra prop.
return React.cloneElement(child, {color: colors[index]});
})
},
render: function(){
return (
<div>{this.renderChildren()}</div>
)
}
})
Child = React.createClass({
render: function(){
return (<div style={{background: this.props.color}}>{'YOLO'}</div>)
}
})
What you are trying to do is not a recommended practice when working with React. Don't modify the DOM yourself, use state and props.
Children components:
var ChildrenA = React.createClass({
render: function() {
return <div style={{color: this.props.color}}>Hello A</div>;
}
});
App:
var App = React.createClass({
render: function() {
return (<div>
<div>Hello {this.props.name}</div>
<div>
<Container/>
</div>
</div>);
}
});
Container:
var Container = React.createClass({
getInitialState: function(){
return {color: "red"}
},
toggle: function(){
this.setState({
color: this.state.color == "red" ? "blue" : "red"
})
},
render: function() {
return (<div onClick={this.toggle}>
<ChildrenA color={this.state.color}/>
<ChildrenB/>
</div>);
}
});
JSFiddle: https://jsfiddle.net/3m8wLcgk/
You might want to check this out: Thinking in React

How to pass function groups as props in React?

I use this simple React component only for example.
I would like to access this.setState() inside the functions 'working' and 'group.notWorking'.
var myComponent = React.createClass({
getInitialState: function() {
return {};
},
working: function() {
this.setState({ test: true }); //this is myComponent
},
group: {
notWorking: function() {
console.log(this); //this is window
}
},
render: function() {
return (
<div>
<ChildComponent working={this.working} group={this.group}/>
</div>
);
},
});
My question is how do you pass functions grouped in an object, or is there any best practice, to avoid passing all the functions one by one to children components.
You need to pass a bound version of it.
<ChildComponent working={this.working} group={this.group.notWorking.bind(this)}/>
If you want to pass the whole group you need to make it a function which returns an object and bind it:
group: function() {
return {
notWorking: function() {
console.log(this);
}.bind(this)
};
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

How to update ReactJS component based on URL / path with React-Router

How can I update a ReactJS component based on URL / path when using React-Router?
The code below works, but is this the correct way to do this? Seems like a lot of code to make a simple update. I was hoping there would be a stateful API call in the router to automatically take care of this scenario.
var MyHomeView = React.createClass({
componentDidMount: function() {
this.props.updateHeader();
},
render: function() {
return (
<div>
<h2>Home</h2>
</div>
);
}
});
var MyAboutView = React.createClass({
componentDidMount: function() {
this.props.updateHeader();
},
render: function() {
return (
<div className="my-page-text">
<h2>About</h2>
</div>
);
}
});
var MyHeader = React.createClass({
mixins: [ CurrentPath ],
getInitialState: function() {
return {
myPath: "about",
classes: "ion-ios7-information"
};
},
updateHeader: function() {
// Classnames refer to www.ionicons.com
if (this.getCurrentPath() === "/") {
this.setState( {myPath: "about" } );
this.setState( {classes: "ion-ios7-information" } );
} else {
this.setState( {myPath: "/" } );
this.setState( {classes: "ion-ios7-rewind" } );
}
},
render: function() {
return (
<Link to={this.state.myPath}>
<i className={this.state.classes} />
</Link>
);
}
});
var App = React.createClass({
updateHeader: function() {
this.refs.header.updateHeader();
},
render: function() {
return (
<div>
<MyHeader ref="header" />
<this.props.activeRouteHandler updateHeader={this.updateHeader} />
</div>
);
}
});
React.renderComponent((
<Routes>
<Route path="/" handler={App}>
<DefaultRoute handler={MyHomeView} />
<Route name="about" handler={MyAboutView} />
</Route>
</Routes>
), document.body);
In react-router 2.0.0 you can use the hashHistory or browserHistory:
browserHistory.listen(function(ev) {
console.log('listen', ev.pathname);
});
<Router history={browserHistory}>{routes}</Router>
This has been updated if you are working with the react-router > v11.0.
You can read the details here
TLDR:
// v0.11.x
var Something = React.createClass({
mixins: [ Router.State ],
render: function () {
var path = this.getPath();
}
});
For the full State API: https://github.com/rackt/react-router/blob/master/doc/04%20Mixins/State.md
This question has been open awhile, and it seems like there should be a more straightforward solution, but I'll take a stab and share what we do in our application.
We are using the Flux architecture, which has the notion of Stores that inform components when their state is updated. In react-router, they have a PathStore that fits into this model nicely, since when the URL changes it can then notify components that care and need to be updated.
The StoreListener we use in our application is publicly available here: https://github.com/odysseyscience/react-flux-suppprt. I should've published to NPM, but haven't yet. I'll do that soon if you think it would be helpful.
Here is how we use our StoreListener to listen for changes on the PathStore from react-router, and how you would use it in your MyHeader:
var StoreListener = require('path/to/StoreListener');
var PathStore = require ('react-router/modules/stores/PathStore');
var MyHeader = React.createClass({
mixins: [
StoreListener(PathStore)
],
getStateFromStores: function() {
return {
path: PathStore.getCurrentPath()
};
},
render: function() {
var path = this.state.path;
var myPath;
var classes;
if (this.state.path === "/") {
myPath = 'about';
classes = 'ion-ios7-information';
} else {
myPath = '/';
classes = 'ion-ios7-rewind';
}
return (
<Link to={myPath}>
<i className={classes} />
</Link>
);
}
});
This starts with a mixin that says "I care about changes to the PathStore". Whenever this store (or any store being listened to) changes, getStateFromStores() is called on your component in order to retrieve the data out of the store that you want available on this.state. If/When that data changes, render() is called again, and you re-read this.state, and apply your changes.
This is exactly our pattern for applying certain "active" CSS classes on header tabs, or anywhere else in the application that depends on the current URL.
Note that we use webpack to bundle our CommonJS modules, so there may be some assumptions about the environment that may/may not work for you.
I didn't answer this question a month ago when I first saw it because I assumed there was a better way, but perhaps our solution can help others with the same issue.

Resources