How can a component do layout of its children? - reactjs

I've been playing with React for a couple of weeks, so I'm far from an expert. This is the problem: I'm building some components that layout their children. That is, aLayoutmay be used as in:
var SomeComponent = React.createClass({
render: function () {
return (
<Layout>
<...child 1/>
<...child 2/>
...
</Layout>
);
}
});
Well, Layout does not own the children, so it cannot modify their props. It cannot clone the children either, as they could contain refs. Could Layout wrap each child in a (properly positioned) div? Would a child with a ref result in the correct update of SomeComponent refs? Is there a better way of doing this?
I thought a bit more about the general problem of parent-child communication and I'm more confused than before. The documentation states:
"For parent-child communication, simply pass props."
In a related part of the documentation we find:
"In React, an owner is the component that sets the props of other components."
So, what gives? It seems to me that only the owner, not the parent, can set props.
This has been discussed here in several posts, but I find no satisfactory answer. Some answers suggest cloning the children in the parent, but we run into problems if the original owner set ref props in its "ownees". In the example above, SomeComponent owns the components passed to Layout as children. If I clone the children in Layout (say, to add a callback) the layout becomes the owner of the clones so SomeComponent refs will not get them.
I am utterly confused.

I'm not entirely sure of what you're asking, but there are things that a wrapping component like your <Layout /> can do to children.
You mention you'd like to wrap each child component in a div. Here's an example of that:
var Layout = React.createClass({
render: function() {
var children = [];
if (this.props.children) {
for (var i=0; i < this.props.children.length; i++) {
var child = this.props.children[i];
children.push(
<div className="child-container">
{child}
</div>
);
}
}
return (
<div className="layout-container">
{children}
</div>
);
}
});
Above you can see that you can access the children of Layout in this.props.children which is just an array of child components. So by using a simple for loop as above, or using this.props.children.map you can wrap each child however you like and render it.

Related

In React how do I have my child tell the parent its key?

In React we are loading a list of children components that define their own ID. We want to then do a pass where we re-arrange the children based on their set internal ID. How do we communicate the internal ID to the parent? Also it would be nice to use that ID as the parent rendering ekey. Note the internal ID does not match the component name.
class Thing1 extends React.Component {
const ID = 'Thing1IDString';
}
class Thing2 extends React.Component {
const ID = 'Thing2IDString';
}
<Parent>
<Thing1 />
<Thing2 />
<Thing3 />
</Parent>
The first thing I'd suggest is trying to make the parent know how to compute the child IDs somehow. If the child IDs come from a database, then make the parent compute them instead of making the children compute them. The lower components in the tree should be less intelligent. Hand them everything they need to know in its final form, so they can simply render it.
The structure you're talking about, where the child has "private" data that the parent needs to act upon, is generally solved in React by:
Storing that data in the parent
Passing the data to the child as a prop
Passing an onDataChanged function to the child, so that the child can tell the parent when the data changes.
This is the "controlled input" pattern. It feels awkward at first, and may seem like there's too much indirection at first glance. But the advantage is that the data flow is very predictable.
Now, if that isn't possible in your case, and you really need the children to "register" with the parent, then you can use props or context to pass a registerChild function to the children. In the child, use its componentDidMount lifecycle method to call the registerChild function with its computed ID. The parent can then keep track of these IDs however it needs to.
Your syntax is kind of confusing. Let's convert the pseudo-example to an actual working ReactJS code sample.
Let's say you have a child(s) component(s):
class Thing1 extends React.Component {
constructor(props) {
super(props);
this.id = 'Thing1IDString';
}
render(){
return (
<p>Child component</p>
);
}
}
If you want to access Thing1's id property from your parent component, there are several ways how to do communicate between components.
It depends what you want to achieve. If you just want to access a child component property, you may use refs:
class Parent extends React.Component {
doSomething() {
// Access anything you need from `this.refs.thing1`
const thing1ID = this.refs.thing1.id;
}
render() {
return (
<div>
<button onClick={this.doSomething.bind(this)}>Get ID</button>
<Thing1 ref="thing1" />
</p>
);
}
}
I've touched above problem here: https://github.com/facebook/react/issues/15320
There are 3 methods you can use to achieve Parent child data transfer (context Api, mutatiin, Reac.Children deep traversal).

ReactJS: Should a large array of objects be passed down to many levels of child components as props?

I'm looking at one of my colleague's ReactJs code and noticed that he is passing an array of custom objects down to 5 levels of child components as props. He is doing this b/c the bottom level child component needs that array's count to perform some UI logic.
At first I was concerned about passing a potentially large array of objects down to this many levels of component hierarchy, just so the bottom one could use its count to do something. But then I was thinking: maybe this is not a big deal since the props array is probably passed by reference, instead of creating copies of this array.
But since I'm kind of new to React, I want to go ahead and ask this question here to make sure my assumptions are correct, and see if others have any thoughts/comments about passing props like this and any better approach.
In regards to the array being passed around I believe it is indeed a reference and there isn't any real downside to doing this from a performance perspective.
It would be better to make the length available on Child Context that way you don't have to manually pass the props through a bunch of components that don't necessarily care.
also it seems it would be more clear to pass only the length since the component doesn't care about the actual objects in the array.
So in the component that holds the array the 5th level child cares about:
var React = require('react');
var ChildWhoDoesntNeedProps = require('./firstChild');
var Parent = React.createClass({
childContextTypes: {
arrayLen: React.PropTypes.number
},
getChildContext: function () {
return {
arrayLen: this.state.theArray.length
};
},
render: function () {
return (
<div>
<h1>Hello World</h1>
<ChildWhoDoesntNeedProps />
</div>
);
}
});
module.exports = Parent;
And then in the 5th level child, who is itself a child of ChildWhoDoesntNeedProps
var React = require('react')
var ArrayLengthReader = React.createClass({
contextTypes: {
arrayLen: React.PropTypes.number.isRequired
},
render: function () {
return (
<div>
The array length is: {this.context.arrayLen}
</div>
);
}
});
module.exports = ArrayLengthReader;
I don't see any problems with passing a big array as a props, even the Facebook is doing that in one of their tutorial about Flux.
Since you're passing the data down to this many lever you should use react contex.
Context allows children component to request some data to arrive from
component that is located higher in the hierarchy.
You should read this article about The Context, this will help you with your problem.

React - how to do jQuery style find() to find children of a DOM node?

I've got a component that returns this:
return (
<div className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
)
I need to do the equivalent of:
handleWheel(event) {
let $canvas = $('.audio-widget').find('canvas');
[...]
}
The canvas is drawn programatically by a 3rd party script, so I can't just slap an ID on it (especially since this is a component and there are several per page).
Excuse the extreme n00b question, I'm brand new to React. Thanks!
This is a good use case for the ref prop. If you give a component a ref prop, e.g. ref="foo", upon rendering that component will be available as e.g. this.refs.foo. Then you can get the rendered DOM node with React.findDOMNode.
render() {
// ...
return (
<div ref="audioWidget" className="audio-widget">
<div
className={this.props.className}
onWheel={this.handleWheel}
/>
{controls}
</div>
);
}
handleWheel(event) {
let canvas = React.findDOMNode(this.refs.audioWidget)
.querySelector('canvas');
// ...
}
You can learn more about refs here: https://facebook.github.io/react/docs/more-about-refs.html
I think, it's a bad way to direct access 'canvas' with selector, the reason is as follows:
When you component changed, the React will do some diff works, then update your dom with a best way, that means, the node you get with selector may be changed to another node just for 'the best way'.
You can build a 'Child Component', and do something in the 'handleWheel' of the 'Parent Component', then communicate with the 'Child Component' through the 'props' for 'Child Component'

ReactJS Why can't I modify children passed into a component

I figured I could do this, but I am getting this error:
TypeError: child.constructor.ConvenienceConstructor is not a function
I have a component in a page, ala:
// this content is in an html page. My component reads in this child, but I can't seem to modify any part of it.. Just diplay it.
<MyComponent prop1="somevalue">
<div className="myclass1"> some child content that is dynamic </div>
</MyComponent>
Now, in my component since that inner child(ren) is dynamic, I need to change that class depending on some condition. But I can't. I get that error I noted above.
I tried this:
var childContent = React.Children.map(this.props.children,
function(child) {
return React.cloneWithProps(child,
{ className: 'myNEWClass' } );
});
I tried cloneElement too, that didn't work either.
Doesn't work. I tried accessing the child directly, ala:
child._store.props.className // but can't seem to change it, seems immutable.
So, how can I change that class up?
thanks,
Props are supposed to be immutable, so instead of passing className as a prop, you make the parent pass in a prop named defaultClass. In your map method, you can add an extra prop, say, overrideClass. Finally, in the render() method of the actual child component, you need some logic to set the className to either overrideClass, or defaultClass. In this way, you don't have to mutate props.

How to get information from children

I'm very new to reactjs (three days, in fact). I want to construct a component that lays out its children according to a policy (implemented by the component) and certain children properties. Some properties are passed directly to the children as props upon creation; some other properties, such as its dimensions, are intrinsic to each child. That is, this Layout component will be used like:
<Layout>
<Child1 propx=... propy=...>...</Child1>
<Child2 propz=...>...</Child2>
...
</Layout>
and the position of the children will depend on propx, propy, propz, ..., and the children sizes.
Well, the Layout component needs to know the values of its children props and their sizes. At first, I gave the children classes a method 'getProperties' to return the relevant properties and I invoked the method in React.Children.forEach(this.props.children, fn) but I found that the object passed to fn is not the component instance so I cannot call getProperties on it. Then I tried to pass each Layout child an extra property 'uploadProperties' with a callback to be called by the child to tell the Layout what it needs to know. I know I cannot call setProps in the children, so I came up with this:
var Layout = React.createClass({
storeChildProperties: function (properties) {
this.childData = this.childData || [];
this.childData.push(properties);
},
render: function () {
var self = this;
var children = [];
React.Children.forEach(this.props.children, function (child) {
var clone = React.addons.cloneWithProps(
child,
{key: child.props.key, uploadProperties: self.storeChildProperties});
children.push(clone);
});
this.props.children = children;
return <div>{this.props.children}</div>;
}
}
It seems to work, but I feel uneasy about two things. First, I don't like attaching 'childData' to the Layout instance (I didn't know where else to put it). Second, I don't know if it's ok to replace this.props.children. Actually, it works even if I don't and I just
return <div>{children}</div>;
Anyway, is this a reasonable way to get info from children?
You can access children's props via this.props.children[0].props.

Resources