I'm trying to pass 1 node as a property of another React component like this:
render: function() {
return (
<div>
<div ref='statusCircle'></div>
<Popover trigger={ this.refs.statusCircle }></Popover>
</div>);
);
}
But in the Popover, this.props.trigger is NULL.
Is there anything wrong with my code?
How can I pass a node-ref to another React component as property?
You've misunderstood the component lifecycle in React.
See here: Link
See this fiddle. https://jsfiddle.net/uzvoruf7/
Open your console, inspect the code for the "Composite" component, and see the lifecycle hooks.
var Popover = React.createClass({
render: function() {
return (<div>This is a pop-over</div>);
}
});
var Composite = React.createClass({
componentDidMount: function() {
console.log(this.refs.statusCircle); //ok, exists.
},
render: function() {
console.log(this.refs.statusCircle); //doesn't exist, sorry.
return (
<div>
<div ref='statusCircle'></div>
<Popover trigger={this.refs.statusCircle}></Popover>
</div>
);
}
});
ReactDOM.render(
<Composite />,
document.getElementById('container')
);
"refs" come alive once the DOM has been rendered.
Therefore, it follows that inside that return statement, the dom has not been rendered yet, hence the reference is null (or all references are null so to speak).
However, inside componentDidMount, you can see that your references are available just as one would expect.
This is a common error: a possible code smell that needs refactoring. Usually (not always), passing down dom-references is indicative of an imperative thought process and is not the React way. I would suggest an improvement but I'm not aware of your use-case.
After typing this I realized it's really not an answer to the original question but instead a follow-up to your request for suggestions in your comment above. If you really don't believe it should stay, I'll delete it but it's too big for a comment, sorry.
Reference material
You must be using an old version of React, so this may not look right to you at first, but you could track that information using state in that parent component by doing
ref={ function ( element ) {
// at the top of render() put
// var self = this;
self.setState({
circleWidth: element.offsetWidth,
circleHeight: element.offsetHeight
})
}
Assuming those values change over time, you'll want to add an event listener for that change (which can be added inside that ref setup) and have it run that setState again when you need it to re-render.
As for <Popover trigger= you'll want to do something to the effect of:
<Popover trigger={this.state.circleWidth > 999} />
...where 999 is whatever your trigger value would be. If it returns true, then you render Popover. If not, you destroy it. Both situations would be handled inside Popover but will not need to touch that div
Of course, good practice in React is to do that comparison at the top of render() and place the result into a variable like isTriggered.
Related
I'm creating a react file tree, and I have the tree setup as a React component. The tree can take a contents prop that is an array of either strings, or other <Tree /> components (this enables the nested file structure UI). These tree components can be nested indefinitely.
I need to register a click event on the children of the nested tree components, but I'm having trouble getting it to work beyond the first level of nesting. A simplified example of what I'm dealing with:
//In App - the top level component
const App = React.createClass({
_handleChildClick () {
console.log("this is where all child clicks should be handled");
},
render () {
return (
<Tree
handleChildClick={this._handleChildClick}
contents={[
<Tree />
]}
/>
);
}
});
//And in the tree component
<div onClick={this.props.handleChildClick}></div>
If you want to see more detail - here's the github repo.
I tried researching this question and saw people using {...this.props} but I'm not sure if that applies to my scenario - if it does, I couldn't get it to work.
Thanks for any help on this.
The reason why the click handling does not work beyond the first level is because your second level Tree component (the one inside the contents array) does not get the appropriate prop handleChildClick passed in. (BTW I think the convention is to call the prop onChildClick while the handler function is called handleChildClick - but I digress.)
Do I understand correctly that you actually want to inform each layer from the clicked component up to the top? For this to happen, you need to extend the props of the tree component that is inside the contents array - it needs to receive the click handler of its parent component. Of course, you cannot write this down statically, so it needs to be done dynamically:
Your Tree component, before actually rendering its children, should extend each of them with the component's click handler, which can be done using the function React.cloneElement (see API documentation and a more detailed discussion). Directly applying this to your component makes things a bit messy, because you are passing the component's children in a prop, so you need to figure out which prop to modify. A bit of a different layout would help you quite a lot here:
<Tree handleChildClick={this._handleChildClick}>
<Tree />
</Tree>
looks nicer IMHO and makes the structure much clearer. You can access the inner components via this.props.children, and cloneElement will be much simpler to use.
So, in your Tree component, you could have a render method like this:
render () {
const newChildren = this.props.children.map(child =>
React.cloneElement(child, {onChildClick: this._handleChildClick}));
return (
<div>{newChildren}</div>
);
}
Please note that this code will not work if you have a mixture of strings and Tree components, therefore my third and last suggestion would be to wrap those strings into a very thin component to allow for easier handling. Alternatively, you can of course do a type comparison inside the map.
I am currently going through the documentation of React.js and have a question about this.props, which according to the docs should be considered immutable and only pushed downwards down the ownership tree since bubbling custom events upwards is discouraged.
Say that I have a UI where the state of a component in the header of the page should be shared with another component that is nested somewhere in the body of the page, let's take a simple checkbox that represents some specific state that will influence the visibility of some nested spans or divs.
The only I way I currently see this achieved is by creating a state property that is pushed downwards from the top element to the child elements.
The two related questions I have with this is:
Does this mean that I should create one component that owns the entire page? (Is rendering the entire page with a single owner component an acceptable thing to do? I understand the concepts of Virtual DOM and diffing so I assume it's not a problem, still I'd like some confirmation in case I miss out on something relevant);
Is it ok to change the property on this.props when clicking the checkbox, in order to re-render the other components on the page? This doesn't make the props immutable (perhaps they mean just that setting new props on this.props down the chain is not accepted to avoid an untransparent stack trace in case of bugs, but changing the value of a state property is?).
Some confirmation would be appreciated.
Thanks.
1) It is fine to have one parent for the whole page, but is not always necessary. It depends on if it is necessary to share the state between components.
2) You never want to alter props via this.props.someValue = newValue. If you need to modify the parent state from a child component, it should always be done with a callback. The example below shows how to share the checkbox state between multiple components using the callback function handleClick to modify the state of is_checked.
JSFiddle of example: https://jsfiddle.net/mark1z/o7noph4y/
var Parent = React.createClass({
getInitialState: function(){
return ({is_checked: 0})
},
handleClick: function(){
this.setState({is_checked: !this.state.is_checked})
},
render: function(){
return (
<div>
<CheckBox is_checked={this.state.is_checked} handleClick={this.handleClick}/>
<OtherComponent is_checked={this.state.is_checked} />
</div>
);
}
});
var CheckBox = React.createClass({
render: function() {
return (
<input type="checkbox" onChange={this.props.handleClick}> Show other component </input>
);
}
});
var OtherComponent = React.createClass({
render: function() {
return (
<div style={{marginTop: 20}}>
{this.props.is_checked ? 'The checkbox is ticked' : 'The checkbox is not ticked'}
</div>
);
}
});
React.render(<Parent />, document.getElementById('container'));
I guess having one master component isn't an issue. The docs suggest that you find the topmost component that can supply it's children with the needed data - and this could easily be the toplevel master component. As I understand this you would have a master component for your main page - that should be the only one that uses state, the children just render what they are given in props. So no, props should not be altered by a child that doesn't own the data, it is the topmost components prerogative to do so. Let's say you have another widget on the page that only cares for a distinct set of data you would make this the root of another tree that fetches data and sets it's state and the props of it's children.
Here is a crappy graph for this situation:
App -(props)-> ItemList -(props)-> Item -(props)-> Photo
+ + |
+ ++++++++++ |----(props)-> LikeButton
+ + |
(fetch) + |
+ + * ---(props)-> Description
++(setState)++
Widget -(props)-> Whether
However it gets more interesting when facebook's graphql is finalized and every component can declare the needed data on it's own, I'm looking forward to it. But until then the toplevel component has to know which data every child needs and all the parent nodes need to hand this data down.
I am getting confused with props and refs in ReactJS. Can anybody explain me the difference between them with proper example.
Thanks in advance.
Props are used to pass parameters which should be static (on the contrary of state). For example you can pass a size or name from an upperView to a lowerView (nested views);
Interesting part on props: https://facebook.github.io/react/docs/transferring-props.html
refs are used to acces the real DOM and not the virtual DOM of react. It's needed when you need to access the real DOM.
This part is interesting :https://facebook.github.io/react/docs/more-about-refs.html
this.setState({userInput: ''}, function() {
// This code executes after the component is re-rendered
React.findDOMNode(this.refs.theInput).focus(); // Boom! Focused!
});
The example above show you how to access a DOM element properly when the state is updated.
Hope it helps.
For sure there are differences between, one mostly use for selecting the DOM, one for getting data as a property, I create the image below and explain few major differences:
Also this the sample of grandparent, parent and child components which using ref and props to pass data, it's a good example to understand when and how they get used, please pay attention how ref helping to get in deeper component by referencing to the element:
function Child(props) {
return (
<div>
<input ref={props.inputRef} />
</div>
);
}
function Parent(props) {
return (
<div>
My input: <Child inputRef={props.inputRef} />
</div>
);
}
class Grandparent extends React.Component {
render() {
return (
<Parent
inputRef={el => this.inputElement = el}
/>
);
}
}
Those are two different things.
Props: Use them to pass any parameters to your component.
Refs: Shortcut for references. These are references to your DOM elements. Use them if you need to access raw DOM element for some reason. For example to add custom event handler via .addEventListener() function.
in addition to the answer above by François Richard, u might wanna read:
https://github.com/uberVU/react-guide/blob/master/props-vs-state.md
because the confusion is more often between state and props.
good luck
I'm working on a project using React.js, and very confused about the composition of React.
http://facebook.github.io/react/docs/multiple-components.html
the link above gives an example. It uses React.creatClass() create three components. A parent component and two child components. The parent component includes the others within it's JSX in the render method.
This example's very clear, but not very 'reusable'. What if I wanna pass in another child in another situation? React.js seems lacking the 'extend' method like Backbone's view.
later, I found that you can pass children components in the React.render(), and use this.props.children to composite.
var Tom = React.createClass({
render: function(){
return(
<a>This is Tom.</a>
)
}
});
var John = React.createClass({
render: function(){
return(
<a>This is John.</a>
)
}
});
var Outter = React.createClass({
componentDidMount:function(){
console.log(this.props.children);
},
render: function(){
return(
<div className="test">
{this.props.children}
</div>
)
}
});
React.render(<Outter><Tom /><John /></Outter>, document.getElementById('main'));
I think that's great but what's the really difference between this method and the example above? is this method the right way to composite components in React.js?
thanks
The difference is just like you said, that you can pass any components you'd like as children of that component. Components that use this.props.children are usually components that acts as wrappers for style and behaviour, but the contents of the component changes for different use cases. Like a popup, where you want the same look and behaviour (like a close button) for every popup, but the contents of the popup is different for every popup.
Components that don't use this.props.children are more like black boxes, they know everything about how they should be rendered and what child components they need. But you can still make them dynamic by passing other props to them.
Components can be also be passed as props. Components are just JS objects, and any JS object can be passed as a prop. But I don't think I've ever seen a use case for doing it. It might make some sense if you have a wrapper component with two or more specific "slots" that should be rendered to. Something like:
var Wrapper = require('./wrapper');
var Header = require('./title');
var Content = require('./content');
var Footer = require('./footer');
var MyComponent = React.createClass({
render() {
return <Wrapper footer={Footer} header={Header} content={Content} />;
}
});
It depends on your use-case, take this two examples of forms:
a login form - almost all login forms have the same structure, username/email, password, a keep me logged in checkbox, and a login button; a LoginForm component doesn't need to be further customised in regards to its structure so it's suitable to be used as is
a signup form - now depending on context you might give more or less fields to the user to complete when signing up, and in this case you use props.children
Why do we have both state and props? Why don't we just have one source of data? I'd like to update a component's props and have it re-render itself and all of its children. Seems simple but I can't figure out how to let a component update its own or its parent's props.
Thanks for any help.
The React philosophy is that props should be immutable and top-down. This means that a parent can send whatever prop values it likes to a child, but the child cannot modify its own props. What you do is react to the incoming props and then, if you want to, modify your child's state based on incoming props.
So you don't ever update your own props, or a parent's props. Ever. You only ever update your own state, and react to prop values you are given by parent.
If you want to have an action occur on a child which modifies something on the state, then what you do is pass a callback to the child which it can execute upon the given action. This callback can then modify the parent's state, which in turns can then send different props to the child on re-render.
To answer the question of why
In React, props flow downward, from parent to child.
This means that when we call ReactDOM.render, React can render the root node, pass down any props, and then forget about that node. It's done with. It's already rendered.
This happens at each component, we render it, then move on down the tree, depth-first.
If a component could mutate its props, we would be changing an object that is accessible to the parent node, even after the parent node had already rendered. This could cause all sorts of strange behaviour, for example, a user.name might have one value in one part of the app, and a different value in a different part, and it might update itself the next time a render is triggered.
To give a fictional example:
// App renders a user.name and a profile
const App = (props) =>
React.createElement('div', null, [
props.user.name,
React.createElement(Profile, props)
])
// Profile changes the user.name and renders it
// Now App has the wrong DOM.
const Profile = ({user}) => {
user.name = "Voldemort" // Uh oh!
return React.createElement('div', null, user.name);
}
// Render the App and give it props
ReactDOM.render(
React.createElement(App, {user: {name: "Hermione"}}),
document.getElementById('app'))
);
We render app. It outputs "Hermione" to the Shadow DOM. We render the Profile, it outputs "Voldemort". The App is now wrong. It should say "Voldemort" because user.name is "Voldemort", but we already output "Hermione", and it's too late to change it.
The value will be different in different parts of the app.
Modifying Props would be two-way-binding
Mutating props would be a form of two-way binding. We would be modifying values that might be relied on by another component higher up the tree.
Angular 1 had this, you could change any data anytime from wherever you were. In order to work, it needed a cyclical $digest. Basically, it would loop around and around, re-rendering the DOM, until all the data had finished propagating. This was part of the reason why Angular 1 was so slow.
In React, state and props serve different goals: state allows a component to maintain some changing values, while props are the mecanism to propagate those values to children.
Children are not allowed to alter by themselves the values they get via props just because React designers find it easier to maintain an application built this way. Their point is that when only one component is allowed to update some piece of state, it is easier to discover who altered it, and find the root of bugs.
the Component itself changes its state, and changes not its own, but the children's props.
<Parent>
<Child name={ this.state.childName } />
</Parent>
Parent can change its own state and change the child name, but it will change the props for his children.
edit1:
for calling events from the child to its parent, you should pass in the child an event handler like so:
var Child = React.createClass({
render: function() {
return (<button onClick={ this.props.onClick }>Hey</button>);
}
});
var Parent = React.createClass({
onChildClick: console.log.bind(console), // will print the event..
render: function() {
return (<Child onClick={ this.onChildClick } />);
}
});
React.renderComponent(<Parent />, document.body);
in this code, when you'll click on the Child's button, it will pass the event to its parent.
the purpose of passing the events is decoupling the components. maybe in your app you need this specific action, but in another app you'll have, you'll use it differently.
My solution was fairly different but some people might run into it. On the Chrome Dev tools, it kept saying that my props were read-only and when I tried passing them down even further, I would get an error. Now, the reason why is because I wasn't invoking a render() method. I was instead calling my component like this:
const Navigation = () =>{
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton />
</ul>
</div>
);
}
I added a render method and it solved my issue of being able to pass props down:
class Navigation extends Component{
render(){
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton user={this.props.user} signedOut={this.props.signedOut} authed={this.props.authed}/>
</ul>
</div>
);
}
}
Hopefully this helps someone.
Contrary to the answers provided here, you actually can update props directly, if you don't mind defying the pedantic circlejerk about "the React way." In React.js, find the following lines of code:
Object.freeze(element.props);
Object.freeze(element);
and comment them out. Voila, mutable props!