Passing a function down to a component through props - REACT - reactjs

I'm trying to pass the 'handleItemClick' function from the 'Dropdown' component, down to they 'Team' component, however, I can't get it past the 'List' component. The strange thing is, is that when I log the props inside the 'List' it says that 'itemClick' is in the 'this.props' object and yet when I set it as a prop on the 'Team' component it says "Cannot read property 'itemClick' of undefined".
Any help would be much appreciated.
Dropdown Component:
var Dropdown = React.createClass({
getInitialState: function(){
return {
display: false
};
},
handleClick: function(e){
this.setState({display: !this.state.display})
},
handleItemClick: function(e){
console.log("Something");
},
render: function(){
return (
<div className="dropdown">
<Button whenClicked={this.handleClick} className="btn-default" title={this.props.data.title} number={this.props.data.teams.length} />
<List teams={this.props.data.teams} display={this.state.display} itemClick={this.handleItemClick}/>
</div>
);
}
});
List Component:
var List = React.createClass({
render: function(){
console.log(this.props)
var teams = this.props.teams.map(function(team){
return <Team key={team} team={team} teamChoice={this.props.itemClick} />
});
var displayStyle = {
display: this.props.display ? 'block' : 'none'
};
return (
<ul style={displayStyle} className="dropdown-menu">
{teams}
</ul>
);
}
});

Kristofen44 was close:
Array.prototype.map() loses the this from your parent scope inside it's callback. But the function includes a variable input for accessing this within it:
var teams = this.props.teams.map(function(team){
return <Team key={team} team={team} teamChoice={this.props.itemClick} />
}, this);

I'm not sure but I think the error resides in List Component's render function when you map team to generate nodes. the map callback function loses the context 'this'. I think you have to bind the callback function to 'this' explicitly.
Like so :
var teams = this.props.teams.map(function(team){
return <Team key={team} team={team} teamChoice={this.props.itemClick} />
}.bind(this)); // <------
BTW, being quite new to react, I don't know if it's good practice to pass an whole object to the attribute 'key' of your Team component... I wonder if it's not better to just pass an id or something like that..
Hope it helps

Related

How to delete an input field from list of input properly

I am trying to handle a list of input dynamically. However when I put some content inside my inputs and delete one from my list I have a undesired behaviour. The correct item is removed but the item in the last position takes the state of the previous item.
var ListItem = React.createClass({
getInitialState: function() {
return {
content: ''
}
},
handleChange: function(e) {
this.setState({
content: e.target.value
})
},
render: function() {
return <div>
<input type="text" onChange={this.handleChange} />
</div>
}
});
var App = React.createClass({
getInitialState : function(){
return {
items : []
}
},
deleteElement: function(index, e) {
this.setState({
items: this.state.items.filter(function (e, i) {
return i !== index;
})
});
},
addElement: function() {
this.setState({
items: this.state.items.concat(<ListItem />)
});
},
render: function() {
var list = this.state.items.map(function(item, i) {
return <li key={ i }>
<p onClick={ this.deleteElement.bind(this, i) }>(-)</p>
<span>{ item }</span>
</li>
}, this);
return <div>
<ul>{ list }</ul>
<p onClick={ this.addElement }>(+)</p>
</div>
}
});
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I think that the issue could be about my list keys but I do not understand why. How could I remove my list elements without altering any states ?
Thanks
When iterating in render, React reuses component instances (for obvious performance reasons). To discriminate between instances, the prop key is used.
In your case when you remove an element, indexes move and therefore you mess with the key -> instance association. So list items after the deleted element are not associated with the right instance anymore, that is why some of your list elements seem to have the state of the previous element.
To avoid this problem you should never use index as key prop. Use a unique identifier for the data instance instead.
In your case, you could generate a unique identifier when adding a new element, and use it as key.
For more information about how lists work in React, read: https://facebook.github.io/react/docs/lists-and-keys.html

ReactJs Mixin not updating/sharing state

Im new to the whole world of React JS.. I have two components which share functionality through a mixin. Its to handle a menu which opens and closes.. It works fine when i click the button and the state turn to true and false. Inside the second component I want to use the close function to close the menu when clicking on the shadow element. The state updates after calling the close function. But when click the button to open menu the state is still false..
Any ideas?
---Mixin---
var menuMixin = {
navIcon: $('#nav-icon'),
menu: $('#menu'),
htmlElement: $('html'),
header: $('header'),
getInitialState: function() {
return {
state: false
};
},
openMenu: function(){
var THIS = this;
var $navIcon = $('#nav-icon'),
$menu = $('#menu'),
$html = $('html'),
$header = $('header');
$menu.show();
$navIcon.addClass('open');
setTimeout(function(){
THIS.switchLogo(true);
$menu.addClass('active');
$html.addClass('fixed');
$header.removeClass('active');
THIS.setState({state: true});
}, 255);
},
closeMenu: function() {
var THIS = this;
var $navIcon = $('#nav-icon'),
$menu = $('#menu'),
$html = $('html'),
$header = $('header');
$menu.removeClass('active');
$navIcon.removeClass('open');
this.switchLogo(false);
setTimeout(function(){
$menu.hide();
$html.removeClass('fixed');
THIS.setState({state: false});
if ( $(document).scrollTop() > 200 ) {
$header.addClass('active');
}
}, 255);
}
};
--Nav Button--
var NavButton = React.createClass({
mixins:[logoSwitchlMixin, menuMixin],
handleClick: function() {
console.log('handleClick -->' + this.state.state);
if ( !this.state.state ) {
this.openMenu();
}
else {
this.closeMenu();
}
},
render: function() {
return (
<button id="nav-button">
<div id="nav-icon" onClick={this.handleClick} ref="icon">
<span></span>
<span></span>
<span></span>
</div>
</button>
)
}
});
-- Sliding Menu --
var MainNav = React.createClass({
mixins:[logoSwitchlMixin, menuMixin],
scroll: function(target) {
$('html, body').animate({
scrollTop: $( '#'+ target ).offset().top
}, 600);
},
scrollSection: function(e){
e.preventDefault();
this.scroll( $(e.target).data('id') );
},
render: function() {
return (
<div id="menu" data-state="false">
<div id="menu_wrapper">
<nav>
<ul>
<li><a data-id="what" title="What We Do" alt="What We Do" onClick={this.scrollSection} >What We Do</a></li>
<li><a data-id="why" title="Why We Do It" alt="Why We Do It" onClick={this.scrollSection}>Why We Do It</a></li>
<li><a data-id="experiance" title="Our Experience" alt="Our Experience" onClick={this.scrollSection}>Our Experience</a></li>
<li><a data-id="how" title="How We Do It" alt="How We Do It" onClick={this.scrollSection}>How We Do It</a></li>
<li><a data-id="competence" title="Competence" alt="Competence" onClick={this.scrollSection}>Competence</a></li>
<li><a data-id="contact" title="Contact" alt="Contact" onClick={this.scrollSection}>Contact</a></li>
</ul>
</nav>
</div>
<div onClick={this.closeMenu} id="shadow_blocker"></div>
</div>
);
}
});
ReactDOM.render(
<MainNav />,
document.getElementById('main-nav')
);
The concept of mixins is code reuse. The same code can be used with multiple components.
But you cant set state for the mixins like you did with menuMixin
The mixin once it is attached to a component and used inside a component will be auto binded with the component and the reference to the component(this) will be autobinded with the mixin function by react.
So you cant have state's to be shared from the mixin. But you can have reference to a dom element and so on but not state. And you can also modify a components state through the setState() method which will update properly, but the state should be present in the component and not in the mixin.

Sending data to an indirect child in React.js on a click event

I have an application that has two major components (Landing and Skills):
App = React.createClass({
render() {
return (
<div>
<Landing />
<Skills category={category}/>
</div>
);
}
});
Within "Landing", I have a SocialMenu component, that has a list of items (the list of items is fed to SocialMenu like: <SocialMenu items={ ['Home', 'Services', 'About', 'Contact us']} />. On click, the item that is clicked is highlighted.
SocialMenu = React.createClass({
getInitialState: function(){
return { focused: 0 };
},
clicked: function(index){
this.setState({focused: index});
},
var self = this;
return (
<div>
<ul className="testblocks">{ this.props.items.map(function(m, index){
var style = '';
if(self.state.focused == index){
style = 'focused';
}
return <li key={index} className={style} onClick={self.clicked.bind(self, index)}>{m}</li>;
}) }
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
);
}
});
What I would like to have happen, is have the index data from SocialMenu, passed to the Skills component.
However, I am not sure how to do that, because SocialMenu is a child of Landing, and not of Skills. (pretty much I would like to keep the list in landing, but have the output of the click of that list be put in Skills).
How would I do this?
You need something like Flux to have communication betweeen components that don't have parent-child relationship:
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
Flux pattern is one of the possible ways to arrange this.
So, for your case, you need to have a event like SELECT_SOCIAL that will pass the index information and your Landing component will have a listener to receive the index information.
You really don't need Flux for this. You probably will at some point, but if this is all you need to do, why don't you move the state up to the App component, and send it Landing & Skills components as props. You would probably also need to pass the clicked handler as a prop to SocialMenu.

pass variable to function via bind in react component

Below is the code snippet from a navigation menu example, which is from this blogpost. What I don't understand is the use of self variable and bind method.
I think this refers to MenuExample when it is stored as self, but I don't know how this changed inside <li>?
Also why is that onClick={this.clicked(index)} doesn't work? In this context what does this refers to?
var MenuExample = React.createClass({
getInitialState: function(){
return { focused: 0 };
},
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>{ this.props.items.map(function(m, index){
var style = '';
if(this.state.focused == index){
style = 'focused';
}
// Notice the use of the bind() method. It makes the
// index available to the clicked function:
return <li className={style} onClick={self.clicked.bind(self, index)}>{m}</li>;
}) }
</ul>
<p>Selected: {this.props.items[this.state.focused]}</p>
</div>
);
}
});
// Render the menu component on the page, and pass an array with menu options
ReactDOM.render(
<MenuExample items={ ['Home', 'Services', 'About', 'Contact us'] } />,
document.getElementById('container')
);
onClick={this.clicked(index)} doesn't work because onClick expects a function, not a result of it. Normally, in a click handler, an event will be passed to it, but here they are using bind() to override that behavior and pass the component and the index as 'this' and the first argument.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

ReactJS: Control a child state from the child and the parent

I have a rather simple problem and I'm not sure how to solve it with React's one way data flow.
Say you have a link in the parent that shows a modal
In the modal, you have an "X" that closes it.
I know I can change the state of the modal from the parent via props
// In the parent
<Modal display={this.state.showModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close">×</a>
...
</div>
And I know how to close the modal, but not both. Not sure how to keep a state that is shared and controllable by both the parent and the child modal.
UPDATE
In trying to keep this as modular as possible, I think the React way would be to store the open/close logic in the modal variable.
var ParentThing = React.createClass({
...
render (
<Modal /> // How can I call this.open in the modal from here?
)
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
I saw this method, but it seems to be a little more than I need to do here. Reactjs: how to modify child state or props from parent?
There are two ways to handle this kind of thing in React:
Make the child "controlled," just like a form input with a value and onChange property, where the owner of the input controls the input.
Make the child "uncontrolled," just like a form input without a value.
The second choice seems faster up front, but just like managing a collection of form inputs in React, the advantage to using fully controlled components becomes apparent as complexity builds and the need to fully describe your UI at any point and time increases. (See this excellent answer from FakeRainBrigand if you're curious exactly why controlled components is better than uncontrolled in most cases.)
However, just like form inputs, there's no reason your component can't be either controlled or uncontrolled. If the user passes a display and onClose property, like Austin Greco's answer, you have a controlled modal, and the parent fully decides when to show or hide the modal.
If the user doesn't, you can skip using the properties and instead delegate to internal state managed by public methods on the modal component:
var ParentThing = React.createClass({
...
render: function() {
return <Modal ref="modal" />;
},
handleSomeClick: function() {
this.refs.modal.open();
}
});
var Modal = React.createClass({
setInitialState: function() {
return {
display: false
}
},
close: function() {
this.setState({ display: false });
},
open: function() {
this.setState({ display: true });
},
render: function() {
return (
<div className={this.state.display ? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
If you like the idea of a controlled Modal component, but don't want to do all the boilerplate typing, you could even go so far as to implement something like the valueLink property for the Modal to simplify this pattern.
var ParentThing = React.createClass({
...
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return { showModal: false };
},
render: function() {
return <Modal displayLink={this.linkState("showModal")} />;
},
handleSomeClick: function() {
this.setState({showModal: true});
}
});
var Modal = React.createClass({
close: function() {
this.props.displayLink.requestChange(false);
},
render: function() {
return (
<div className={this.props.displayLink.value? "show" : "hide"}>
<a className="close" onClick={this.close}>×</a>
</div>
)
}
});
(See my blog post on creating custom components that work with linkState/valueLink for more info.)
So now you get the benefit of using a fully parent-controlled Modal, but you've removed a portion of the boilerplate around creating a function that sets the value to false and passing it to the modal.
You could pass a callback as a prop to the child component:
// In the parent
<Modal display={this.state.showModal} onClose={this.closeModal} />
// In the modal
<div className={this.props.display ? "show" : "hide"}>
<a className="close" onClick={this.props.onClose}>×</a>
...
</div>
Then when you click the close button on the child, it will call the function of the parent

Resources