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.
Related
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).
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.
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.
I have come across a problem about states based on properties.
The scenario
I have a Component parent which creates passes a property to a child component.
The Child component reacts according to the property received.
In React the "only" proper way to change the state of a component is using the functions componentWillMount or componentDidMount and componentWillReceiveProps as far as I've seen (among others, but let's focus on these ones, because getInitialState is just executed once).
My problem/Question
If I receive a new property from the parent and I want to change the state, only the function componentWillReceiveProps will be executed and will allowed me to execute setState. Render does not allow to setStatus.
What if I want to set the state on the beginning and the time it receives a new property?
So I have to set it on getInitialState or componentWillMount/componentDidMount. Then you have to change the state depending on the properties using componentWillReceiveProps.
This is a problem when your state highly depends from your properties, which is almost always. Which can become silly because you have to repeat the states you want to update according to the new property.
My solution
I have created a new method that it's called on componentWillMount and on componentWillReceiveProps. I have not found any method been called after a property has been updated before render and also the first time the Component is mounted. Then there would not be a need to do this silly workaround.
Anyway, here the question: is not there any better option to update the state when a new property is received or changed?
/*...*/
/**
* To be called before mounted and before updating props
* #param props
*/
prepareComponentState: function (props) {
var usedProps = props || this.props;
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === usedProps.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
componentWillMount: function () {
this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
/*...*/
I feel a bit stupid, I guess I'm loosing something...
I guess there is another solution to solve this.
And yeah, I already know about this:
https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html
I've found that this pattern is usually not very necessary. In the general case (not always), I've found that setting state based on changed properties is a bit of an anti-pattern; instead, simply derive the necessary local state at render time.
render: function() {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === this.props.currentQuestion.id;
});
return ...; // use currentResponses instead of this.state.currentResponses
}
However, in some cases, it can make sense to cache this data (e.g. maybe calculating it is prohibitively expensive), or you just need to know when the props are set/changed for some other reason. In that case, I would use basically the pattern you've written in your question.
If you really don't like typing it out, you could formalize this new method as a mixin. For example:
var PropsSetOrChangeMixin = {
componentWillMount: function() {
this.onPropsSetOrChange(this.props);
},
componentWillReceiveProps: function(nextProps) {
this.onPropsSetOrChange(nextProps);
}
};
React.createClass({
mixins: [PropsSetOrChangeMixin],
onPropsSetOrChange: function(props) {
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
// ...
});
Of course, if you're using class-based React components, you'd need to find some alternative solution (e.g. inheritance, or custom JS mixins) since they don't get React-style mixins right now.
(For what it's worth, I think the code is much clearer using the explicit methods; I'd probably write it like this:)
componentWillMount: function () {
this.prepareComponentState(this.props);
},
componentWillReceiveProps: function (nextProps) {
this.prepareComponentState(nextProps);
},
prepareComponentState: function (props) {
//set data on state/template
var currentResponses = this.state.candidatesResponses.filter(function (elem) {
return elem.questionId === props.currentQuestion.id;
});
this.setState({
currentResponses: currentResponses,
activeAnswer: null
});
},
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.