ReactJS Cortex Object updated by render not invoked? - javascript

I am trying to implement Emberjs's Todo app as a practice exercise for Cortex by mquan on github. I am currently implementing the "All", "Active", "Completed" filter where clicking an anchor will result in the anchor being highlighted (class added).
I created the following:
var filtercortex = new cortex([
{title:'all', selected:true, key:1},
{title:'completed', selected:false, key:2},
{title:'active', selected:false, key:3}
]);
With the following render function (in the parent):
render: function() {
var filters = filterCortex.map(function(filter) {
return (
<li>
<FilterAnchor cortex={filterCortex} filter={filter} />
</li>
)
});
...
return ...
<ul id='filters'>
{filters}
</ul>
And FilterAnchor's definition:
var FilterAnchor = React.createClass({
handleClick: function() {
var that = this;
this.props.cortex.forEach(function(filter) {
if (filter.key.getValue() == that.props.filter.key.getValue()) {
console.log(filter.title.getValue(), true);
filter.selected.set(true);
} else {
console.log(filter.title.getValue(), false);
filter.selected.set(false);
}
});
return false;
},
render: function() {
var className = (this.props.filter.selected.getValue()) ? 'selected' : '';
return (
<a className={className} href="#" onClick={this.handleClick}>
{this.props.filter.title.getValue()}
</a>
)
}
});
right now, I do not see the class 'selected' being applied to the anchor links when I am clicking.
However, upon investigation I notice this:
Clicking "All":
All true
Completed false
Active false
Clicking "Completed":
All true
Completed false
Active false
So I am certain that the objects inside filtercortex has been updated properly (you can open up firebug to check). However, FilterAnchor.render is not being triggered.
Is this a bug?
Source code: https://github.com/vicngtor/ReactTodo/blob/cortex/script.jsx

The sample at the top of the Cortex readme has this at the bottom:
orderCortex.on("update", function(updatedOrder) {
orderComponent.setProps({order: updatedOrder});
});
Is there an equivalent section in your code? If not, then the problem is that the update event for the cortex data store isn't set to trigger an update of the view, which is made through a call to setProps on the top level React component in this example.

Related

how to control child elements by onClick from parent in reactjs?

crafting basic app in react as following:
parent container receive state by ajax and contains four columns, left column - all messages items, second column message body (should be shown when message element is clicked as well as controls), next - message controls (next, prev) and action type:
how to to properly attach a controls to children elements for instance for onClick to message element? here is the snippet of a parent:
var ModerationContainer = React.createClass({
getInitialState: function () {
return {data: []};
},
componentDidMount: function () {
...
},
LoadMessagesFromApi: function () {
jQuery.ajax({
... // loads messages from json api into state
});
},
testor: function () {
alert();
},
render: function () {
var allMessageItems = this.state.data.map(function (message) {
return (
<MessageItem id={message.id} key={message.id} onClick={this.testor}/>
);
}, this);
return (
<div>
<div className="col-md-2 messageColumn">
{allMessageItems}
</div>
<MessageBodyColumn/>
<ControlsColumn />
<BlockColumn />
</div>
);
}
});
No onclick event is executed after i click message block althrought I attached this to map while rendering messages block, what did i wrong ?
Also, how it is possible to auto select first message item if none of them clicked ?
Any hints or links on tutorials from experienced with react people much appreciated
I would right the MessageItem on the parent like
<MessageItem key={message.id} onClick={this.testor.bind(this, message.id) }/>
Then inside your MessageItem component you can take the onClick handler from the pros, lets say MessageItem is a div your render function could be like
render()
{
const onClick = this.props.onClick;
const label = `Message${this.props.key}`;
return( <div onClick={ onClick }> { label }</div>)
}
and if you write your testor like
testor: function ( id ) {
alert( id );
}
You can see the id of the clicked message.

React: Unable to access child props in parent's event handler

I'm using React to create a UI and I have a parent component and a child component, something along these lines:
// Child component
var ListItem = React.createClass({
render: function() {
var link_details = (
<div>
Start Date: {this.props.my_data.start_date}<br/>
End Date: {this.props.my_data.end_date}<br/>
</div>
);
return (
<li>
<a onClick={this.props.clickHandler}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
)
}
});
// Parent component
var Sidebar = React.createClass({
getInitialState: function() {
return {
my_data: [],
};
},
handleListItemClick: function(e){
console.log(e.target);
console.log(e.target.props);
},
render: function() {
var myLinks = this.state.my_data.map(function(mylink) {
return (
<ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick} />
);
}.bind(this));
return (
<div>
<ul className="nav nav-sidebar">
{ myLinks }
</ul>
</div>)
}
});
I want the click event on the child to trigger the parent's handler so that the parent can update its state based on what was clicked in the child. While the code I have above works, and the parent's handler is called, I am unable to access any of the child component's props. I'm not sure if that's by design and I should pass data from the child to the parent in a different way, or if I'm doing something wrong. I'm still very new to React, so any advice is appreciated. Thanks!
You can not do that but you can pass data from child to parent via callback
<li>
<a onClick={this.props.clickHandler.bind(null,this.props.my_data.name)}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
or using arrow function if you are using es6
<li>
<a onClick={() => this.props.clickHandler(this.props.my_data.name)}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
Edit
Why passing null?
Things to remember:
Automatic binding methods to 'this' happens when your component mounts.
There are two conditions
1.Calling a callback passed from parent component to a child component
When we directly pass functions (e.g. this.clickHandler) to a child component without worrying about the value of 'this' when the function is actually called.
React then the replaces the standard Function.prototype.bind method with its own function to help stop you from doing anything silly (like trying to change the already-bound value of 'this'), so you instead have to pass 'null' to say "I understand this will only alter the arguments".
2.Calling a function defined within same component
React does not do this for function calls within the same component
Rules for binding
If you want to set the first argument by calling .bind on a function...
passed in via props, pass null as the first argument e.g.
this.props.funcName.bind(null, "args")
taken from 'this', pass 'this' as the first argument e.g.
this.funcName.bind(this, "args")
You can do so:
var ListItem = React.createClass({
clickItem: function (e) {
this.props.clickHandler(e, this.props.my_data); // now you can pass any data to parent
},
render: function() {
var link_details = (
<div>
Start Date: {this.props.my_data.start_date}<br/>
End Date: {this.props.my_data.end_date}<br/>
</div>
);
return (
<li>
<a onClick={this.clickItem}>
{ this.props.my_data.name }
</a>
{link_details}
</li>
)
}
});
I took a look at the answer on Pass props to parent component in React.js and came up with the following:
// Parent component
var Sidebar = React.createClass({
getInitialState: function() {
return {
my_data: [],
};
},
handleListItemClick: function(data_passed, e){
console.log(data_passed);
},
render: function() {
var myLinks = this.state.my_data.map(function(mylink) {
return (
<ListItem key={mylink.id} my_data={mylink} clickHandler={this.handleListItemClick.bind(null, mylink.id)} />
);
}.bind(this));
return (
<div>
<ul className="nav nav-sidebar">
{ myLinks }
</ul>
</div>)
}
});
This does seem to work- I'd be interested in seeing other solutions and which one is the "best" and why.

React-router unintended reload on transitionTo (Chrome)

In a (single-page) application implemented on top of React and React Router, we are observing a strange pattern when using the router.transitionTo method.
Apologies for the extra verbose context, but don't want to miss something pertinent.
Let's assume that we have the following Router initialization, which is called from the single "physical" page of the application:
define([], function () {
var _Root = React.createClass({
render : function () {
return React.createElement(React.addons.CSSTransitionGroup, {/**/},
React.createElement(Router.RouteHandler, {/**/}));
}
});
var _NotFoundScreen = React.createClass({/**/});
var _NoDefaultScreen = React.createClass({/**/});
var routes = (
React.createElement(Router.Route, {
handler : _Root,
path : "/SampleApplication/"
},
React.createElement(Router.Route, {
path : "Transfer",
handler : Transfer
}),
React.createElement(Router.Route, {
path : "Home",
handler : Home
}),
React.createElement(Router.DefaultRoute, {
handler : Home || _NoDefaultScreen
}),
React.createElement(Router.NotFoundRoute, {
handler : _NotFoundScreen
})));
return {
init : function () {
var router = Router.create({
routes : routes,
location : Router.HistoryLocation
});
router.run(function (rootClass, state) {
if (state.routes.length === 0) {
window.location = state.path;
}
React.render(React.createElement(rootClass, state), document.getElementById("reactContainer"));
});
}
}
});
And then we have the "Transfer" page with something along the lines of:
define([], function () {
var View = (function (_super) {
/*(...)*/
function View() {
/*(...)*/
}
View.prototype.render = function () {
/*(...)*/
return React.DOM.div(null, React.createElement(Button.Button, {
enabled : true,
onClick : function () {
router.transitionTo("/SampleApplication/Home");
},
style : "btn",
visible : true
}, "B1"), React.DOM.br(), "form:", React.createElement(Form.Form, {
style : "form",
visible : true
}, React.createElement(Button.Button, {
enabled : true,
onClick : function () {
router.transitionTo("/SampleApplication/Home");
},
style : "btn btn-primary",
visible : true
}, "B2")));
};
return View;
})(/*(...)*/);
return View;
});
So, we have 2 buttons on the Transfer page - B1 and B2. B2 is wrapped by a "form" element. Both buttons are capable of "navigating" to the home page, when clicked.
Navigation through B1 works as expected.
Navigation through B2 reveals some peculiar behavior.
browser url: host/SampleApplication/Transfer
we click B2
we "navigate" to host/SampleApplication/Home and see the page content for a fraction of a second
fraction of a second later, browser url changes to host/SampleApplication/Home?
we get a white screen (as if we're loading/accessing the application for the first time and it is initializing)
we get the rendered page # host/SampleApplication/Home?
I have been trying to find the issue for a while now and no amount of debugging seems to be producing any results.
The execution flows for the navigation from both B1 and B2 are identical (down to the point where the React Router calls location.push(path)).
Furthermore, this "only" happens with Chrome (desktop and mobile), Opera (mobile) and Android stock browser, while for Firefox (desktop and mobile) both B1 and B2 are able to navigate from one page to another without any extra reloads nor "leaving" the single physical page of the application.
I have been unable to find any pertinent information about similar behavior patterns that could explain what could be going on.
If anyone could provide some insight on what could be happening here, it would be most appreciated.
With best regards,
SYG
Ok, the source of the problem was identified as being the default type of button element - submit (link to the HTML recommendation).
Since the button was being rendered within a form element, the "onClick" of the button was implicitly submitting the form, causing a page reload to get queued. The "onClick" of the button executes router.transitionTo, effectively navigating to the target page and the "queued" page reload from the form submit gets executed immediately afterwards, causing the application to re-initialize.
The behavior was "fixed" by changing the type of the button element to button.

How to show drop menu and hide others in React.js

I just want to know the best way to proceed (don´t need the code, just the way to do it). I´m trying to show a dropdown menu when I click on it´s LI element.
var Balloon = React.createClass({displayName: "Balloon",
getInitialState: function() {
return { shaded: false };
},
handleClick: function(event) {
this.setState({ shaded: !this.state.shaded });
},
render: function() {
var panel = this.state.shaded ? React.createElement(BalloonPanel, {type: this.props.type, data: this.props.data}) : "";
return (
React.createElement("li", {onClick: this.handleClick},
React.createElement("a", {href: ""}),
React.createElement("div", {hidden: true}),
React.createElement("div", null,
React.createElement("div", {class: "triangle"}, " "),
panel
)
)
);
}
});
Here is the complete code:
Thanks in advance.
So assuming your drop downs are all reliant upon one another, i.e.. when you click one the others close etc... than they should all be built with the same object and ascribe to a click event that passes this to the parent.
var ParentComponent = React.createClass({
clicked: function () {
alert("you clicked me");
},
return: function () {
render (
<ReactListChild onClick={this.props.clicked.bind(this)} />
)
});
Keep in mind you need to use the bind method in order for the children to know which one was clicked (to take the appropriate action)
So summing this up, your parent component should have a state variable saying which one to show and set some sort of variable, possibly give it the name of the element or something. that way if that element is not listed as shown in state the others will remain closed.
fyi, I did not test this code, it's just a rough idea. Most likely you will do some sort of for loop to render many of these child elements. Remember the bind, or you'll get burned.

How to navigate via clickHandlers?

I just started learning React and I'm stuck in the following scenario.
There is an input field and on clicking the search button it takes the input value and redirect to /search/search-query/some-action
I have setup the path, defined the route to the correct view. And, I was able to do hit this path using href links. But my actual requirement is to have a button and take user to this path via onClick handler.
Searched a lot and found multiple (vague) solutions like, react-navigate-mixin etc. But I couldnt find any documentation around its usages.
Can anyone point me in the right direction?
I'm gonna make the assumption that you're not building a single page app and using something along the lines of React router. And that what you need to do is simply navigate to a url based on the input.
There are two main ways of doing depending on wether you want to:
Style an <a> as your button:
var Component = React.createClass({
getInitialState: function() { return {query: ''} },
queryChange: function(evt) {
this.setState({query: evt.target.value});
},
_buildLinkHref: function() {
return '/search/'+this.state.query+'/some-action';
},
render: function() {
return (
<div className="component-wrapper">
<input type="text" value={this.state.query} />
<a href={this._buildLinkHref()} className="button">
Search
</a>
</div>
);
}
});
This way you're keeping the query (value of the input) in the state. And whenever the input changes is automatically changes the href of the link.
Use a <button> and redirect programatically:
var Component = React.createClass({
getInitialState: function() { return {query: ''} },
queryChange: function(evt) {
this.setState({query: evt.target.value});
},
handleSearch: function() {
window.location = '/search/'+this.state.query+'/some-action';
},
render: function() {
return (
<div className="component-wrapper">
<input type="text" value={this.state.query} />
<button onClick={this.handleSearch()} className="button">
Search
</button>
</div>
);
}
});
This way you handle the redirect programatically by setting your desired location to window.location.
Adding on from the existing answer may need to bind your function like so:
constructor(props) {
super(props);
this.handleSearch = this.handleSearch.bind(this);
}

Categories