ReactJs Mixin not updating/sharing state - reactjs

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.

Related

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

Passing a function down to a component through props - REACT

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

Accessing deeper array keys

I am trying to create a navigation menu which supports dropdown items. I have created
var menuItems={
items:[
{
title:"Home",
id:1
},
{
title:"Download App",
children:["iOS","Android","Windows"],
id:2
},
{
title:"About",
id:3
}
]
};
This is my Menu Prototype Class:
var MenuProto = React.createClass({
render: function(){
return <li><a>{this.props.title}</a>
<ul className="dropdown-menu">
<li><a>{this.props.children}</a></li>
</ul>
</li>
}
});
And this is where it is called:
var Menu = React.createClass({
render : function(){
var fullmenu = this.props.items.map(function(item){
return <MenuProto {...item}/>
});
return <ul className="nav navbar-nav navbar-right">
{fullmenu}
</ul>
}
});
Apparently this.props.children gets all the array and uses renders it on the menu while I need it to get them one by one. I have tried creating a var similar to fullmenu which would iterate through childrens of this.props.children one by one but I can't figure out how.
I would also need a hint on how to make a conditional to see if an object has or doesnt have the children key.
Any help would be greatly appreciated.
You can iterate on this.props.children using the React.Children utilities:
var MenuProto = React.createClass({
render: function(){
return <li><a>{this.props.title}</a>
<ul className="dropdown-menu">
{React.Children.map(this.props.children, function(child) {
return <li key={child.id}><a>{child.title}</a></li>
})}
</ul>
</li>
}
});
Since you want to support nested items, you'll need some additional custom logic.
You can check to see if an object has a children key by simply testing for the existence of whatever.children; if it doesn't have that key, it will be a falsy value (specifically undefined).

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

Angularjs toggle image onclick

I'm trying to toggle a button image when a user clicks it. I prefer to use angularjs instead of jquery if possible. Right now I have a working version which toggles the image when clicked, the only problem is it changes ALL the images on click. How do I reduce the scope or pass in the src attribute for the img element?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage()"><img id="followbutton" ng-src="{{followBtnImgUrl}}" /></a>
</div>
</div>
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/img1'
$scope.toggleImage = function () {
if ($scope.followBtnImgUrl === '/img1.jpg') {
$scope.followBtnImgUrl = baseUrl + '/img2.jpg';
} else {
$scope.followBtnImgUrl = 'img1.jpg';
}
}
});
Can I pass in the img src attribute the function like toggleImage(this.img.src) or similar?
<div ng-repeat="merchant in merchants">
<div class="followrow">
<a ng-click="toggleImage(merchant)"><img id="followbutton" ng-src="{{merchant.imgUrl}}" />
</a>
</div>
</div>
.
app.controller('FollowCtrl', function CouponCtrl($scope) {
$scope.followBtnImgUrl = '/sth.jpg'
$scope.merchants = [{imgUrl: "img1.jpg", name:"sdf"},
{imgUrl: "img2.jpg", name: "dfsd"}];
$scope.toggleImage = function(merchant) {
if(merchant.imgUrl === $scope.followBtnImgUrl) {
merchant.imgUrl = merchant.$backupUrl;
} else {
merchant.$backupUrl = merchant.imgUrl;
merchant.imgUrl = $scope.followBtnImgUrl;
}
};
});
What you want is a new scope for each followrow. As your code stands, there's only one scope that all of the followrows are referencing.
A simple answer is to create a new controller that you attach to each followrow:
<div class="followrow" ng-controller="ImageToggleCtrl">...</div>
And then move the image toggling logic to that new controller:
app.controller('ImageToggleCtrl', function($scope) {
$scope.followBtnImgUrl = '/img1';
$scope.toggleImage = function() { /* the toggling logic */ };
});
Now, a new scope will be instantiated for each row, and the images will toggle independently.
I just added two clickable images:
<div ng-app="FormApp" ng-controller="myController" max-width="1150px;" width="1150px;" >
<input ng-show="ShowDown" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactShow.png" ng-click="ShowHide()"/>
<input ng-show="ShowUp" type="image" style="width:250px; height:40px;" src="~/Content/Images/contactHide.png" ng-click="ShowHide()" />
</div>
They toggle eachothers visibility. At page load one is visible, one is not, and both clickable images call the same function:
<script type="text/javascript">
var app = angular.module('FormApp', [])
app.controller('myController', function ($scope) {
$scope.ShowDown = true;
$scope.ShowUp = false;
$scope.ShowHide = function () {
$scope.ShowDown = $scope.ShowDown ? false : true;
$scope.ShowUp = $scope.ShowUp ? false : true;
}
});
</script>

Resources