React: Access child element with findDOMNode throws error - reactjs

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

Related

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>
);
}
});

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

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.

How do I set state of sibling components easily in React?

I have got the beginnings of a clickable list component that will serve to drive a select element. As you can see from the below, onClick of the ListItem, I'm passing the state of a child element (ListItem in this case) to the parents (SelectableList, and CustomSelect component). This is working fine. However, what I would also like to do is change the state of the sibling components (the other ListItems) so that I can toggle their selected states when one of the ListItems is clicked.
At the moment, I'm simply using document.querySelectorAll('ul.cs-select li) to grab the elements and change the class to selected when it doesn't match the index of the clicked ListItem. This works - to an extent. However, after a few clicks, the state of the component has not been updated by React (only by client side JS), and things start to break down. What I would like to do is change the this.state.isSelected of the sibling list items, and use this state to refresh the SelectableList component. Could anyone offer a better alternative to what I've written below?
var React = require('react');
var SelectBox = require('./select-box');
var ListItem = React.createClass({
getInitialState: function() {
return {
isSelected: false
};
},
toggleSelected: function () {
if (this.state.isSelected == true) {
this.setState({
isSelected: false
})
} else {
this.setState({
isSelected: true
})
}
},
handleClick: function(listItem) {
this.toggleSelected();
this.props.onListItemChange(listItem.props.value);
var unboundForEach = Array.prototype.forEach,
forEach = Function.prototype.call.bind(unboundForEach);
forEach(document.querySelectorAll('ul.cs-select li'), function (el) {
// below is trying to
// make sure that when a user clicks on a list
// item in the SelectableList, then all the *other*
// list items get class="selected" removed.
// this works for the first time that you move through the
// list clicking the other items, but then, on the second
// pass through, starts to fail, requiring *two clicks* before the
// list item is selected again.
// maybe there's a better more "reactive" method of doing this?
if (el.dataset.index != listItem.props.index && el.classList.contains('selected') ) {
el.classList.remove('selected');
}
});
},
render: function() {
return (
<li ref={"listSel"+this.props.key}
data-value={this.props.value}
data-index={this.props.index}
className={this.state.isSelected == true ? 'selected' : '' }
onClick={this.handleClick.bind(null, this)}>
{this.props.content}
</li>
);
}
});
var SelectableList = React.createClass({
render: function() {
var listItems = this.props.options.map(function(opt, index) {
return <ListItem key={index} index={index}
value={opt.value} content={opt.label}
onListItemChange={this.props.onListItemChange.bind(null, index)} />;
}, this);
return <ul className="cs-select">{ listItems }</ul>;
}
})
var CustomSelect = React.createClass({
getInitialState: function () {
return {
selectedOption: ''
}
},
handleListItemChange: function(listIndex, listItem) {
this.setState({
selectedOption: listItem.props.value
})
},
render: function () {
var options = [{value:"One", label: "One"},{value:"Two", label: "Two"},{value:"Three", label: "Three"}];
return (
<div className="group">
<div className="cs-select">
<SelectableList options={options}
onListItemChange={this.handleListItemChange} />
<SelectBox className="cs-select"
initialValue={this.state.selectedOption}
fieldName="custom-select" options={options}/>
</div>
</div>
)
}
})
module.exports = CustomSelect;
The parent component should pass a callback to the children, and each child would trigger that callback when its state changes. You could actually hold all of the state in the parent, using it as a single point of truth, and pass the "selected" value down to each child as a prop.
In that case, the child could look like this:
var Child = React.createClass({
onToggle: function() {
this.props.onToggle(this.props.id, !this.props.selected);
},
render: function() {
return <button onClick={this.onToggle}>Toggle {this.props.label} - {this.props.selected ? 'Selected!' : ''}!</button>;
}
});
It has no state, it just fires an onToggle callback when clicked. The parent would look like this:
var Parent = React.createClass({
getInitialState: function() {
return {
selections: []
};
},
onChildToggle: function(id, selected) {
var selections = this.state.selections;
selections[id] = selected;
this.setState({
selections: selections
});
},
buildChildren: function(dataItem) {
return <Child
id={dataItem.id}
label={dataItem.label}
selected={this.state.selections[dataItem.id]}
onToggle={this.onChildToggle} />
},
render: function() {
return <div>{this.props.data.map(this.buildChildren)}</div>
}
});
It holds an array of selections in state and when it handles the callback from a child, it uses setState to re-render the children by passing its state down in the selected prop to each child.
You can see a working example of this here:
https://jsfiddle.net/fth25erj/
Another strategy for sibling-sibling communication is to use observer pattern.
The Observer Pattern is a software design pattern in which an object can send messages to multiple other objects.
No sibling or parent-child relationship is required to use this strategy.
Within the context of React, this would mean some components subscribe to receive particular messages and other components publish messages to those subscribers.
Components would typically subscribe in the componentDidMount method and unsubscribe in the componentWillUnmount method.
Here are 4 libraries that implement the Observer Pattern. The differences between them are subtle - EventEmitter is the most popular.
PubSubJS: "a topic-based publish/subscribe library written in JavaScript."
EventEmitter: "Evented JavaScript for the browser." It's actually an implementation of a library that already exists as part of nodejs core, but for the browser.
MicroEvent.js: "event emitter microlibrary - 20lines - for node and browser"
mobx: "Simple, scalable state management."
Taken from: 8 no-Flux strategies for React component communication which also is a great read in general.
The following code helps me to setup communication between two siblings. The setup is done in their parent during render() and componentDidMount() calls.
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}

How can I disable animation in a reactjs component first time

I have a component with a list of items. I want each time I add or remove an item from the list to have animation. Everything it's OK with that.
My only problem is that I want to disable the animation first time when the component initialize.
http://jsfiddle.net/kb3gN/6887/
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var Hello = React.createClass({
getInitialState: function () {
return {items: []};
},
addColor:function(){
this.setState({items:this.state.items.concat('new color')});
},
componentDidMount: function () {
var items = ['blue', 'red', 'green'];
this.setState({items: items});
},
render: function () {
var contentList = this.state.items.map(function (i,k) {
return (<li>{i}</li>);
});
return (
<div>
Hello <button onClick={this.addColor}>add</button>
<ReactCSSTransitionGroup transitionName="example">
{contentList}
</ReactCSSTransitionGroup>
</div>
)
}
});
React.render(<Hello name="World" />, document.body);
So, how can I make the animation appear only when changing the list and not ont the very first setState from didMount method?
Using componentWillMount instead of componentDidMount it will work perfectly as shown in this fiddle : http://jsfiddle.net/ghislaindj/jpuyd3gb/1/
However, if your real code must fetch the data in DidMount, you should use ssorallen comment.

Sending events between components

Consider the ToDo App example on the React homepage. For posterity, here's a fiddle and the code is at the end of this post.
Now say we decide to upgrade this app with w simple features:
Each todo item will have not only text, but also a "done" attribute. You can click on an item and it will toggle the "done" state, and perhaps add strikethrough styling when it is done.
At the very bottom, there will be text indicating the number of "Done", eg, "2 items done, 3 left to do"
The problem is that the state of the items is maintained in TodoApp, not in TodoList. So we'd like to add an onClick={something} to the <li> element in TodoList's render method. But we want that click event to be handled by TodoApp, which would then change the state of the item, and cause everything to re-render. If we wanted to approach it like this, how could we do it?
We could also create a TodoItem component to be called by TodoList, and push the statefulness down into that. This would allow the click to be handled by the TodoItem, but now we would need a way to share the TodoItems' states with the component indicating the number of items done and still todo.
In a nutshell, I'd like to know how components can send events to each other, because I think just knowing that would allow solutions to both problems.
React ToDo App
/** #jsx React.DOM */
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.renderComponent(<TodoApp />, document.body);
The idiomatic way to do this is to pass a callback down to TodoList:
Live demo: http://jsbin.com/zeqizene/1/edit
I've changed TodoList to look like this:
var TodoList = React.createClass({
handleDoneToggle: function(i) {
this.props.onDoneToggle(i);
},
render: function() {
var createItem = function(item, i) {
return <li onClick={this.handleDoneToggle.bind(null, i)}>
{item.text}
{item.done && " (done)"}
</li>;
};
return <ul>{this.props.items.map(createItem, this)}</ul>;
}
});
When an item is clicked, TodoList will call its own onDoneToggle function, so TodoApp can modify the state appropriately.
See also Editing a rich data structure in React.js.

Resources