Where is this variable coming from in this react script - javascript

I'm starting out looking at react, I've seen a good example here https://scotch.io/tutorials/learning-react-getting-started-and-concepts
however I'm not sure where 'item' is coming from, it doesn't appear to be declared anywhere. I've highlighted item in the code below.
/** #jsx React.DOM */
var List = React.createClass({
render: function(){
return (
<ul>
{
this.props.items.map(function(**item**) {
return <li key=**{item}>{item}**</li>
})
}
</ul>
)
}
});
var FilteredList = React.createClass({
filterList: function(event){
var updatedList = this.state.initialItems;
updatedList = updatedList.filter(function(**item**){
return **item**.toLowerCase().search(event.target.value.toLowerCase()) !== -1;
});
this.setState({items: updatedList});
},
getInitialState: function(){
return {
initialItems: [
"Apples",
"Broccoli",
"Chicken",
"Duck",
"Eggs",
"Fish",
"Granola",
"Hash Browns"
],
items: []
}
},
componentWillMount: function(){
this.setState({items: this.state.initialItems})
},
render: function(){
return (
<div className="filter-list">
<input type="text" placeholder="Search" onChange={this.filterList}/>
<List items={this.state.items}/>
</div>
);
}
});
React.renderComponent(<FilteredList/>, document.getElementById('mount-point'));

updatedList is an array.
Array.filter takes a function as an argument that will be passed in a variable. They are using item as that variable name.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Related

Correct way to rerender my React component?

I'm pretty new to React (coming from Angular 1), and have been playing around with it somewhat. I have a test script that loops through a multidimensional object, and binds it to the dom.
I then add a new item to the object wrapped in a setTimeout. Is calling the ReactDOM.render below that the best way to rerender the React component?
var items = [
{ name: 'Matt', link: 'https://google.com' },
{ name: 'Adam', link: 'https://bing.com' },
{ name: 'Luke', link: 'https://yahoo.com' },
{ name: 'John', link: 'https://apple.com' }
];
var RepeatModule = React.createClass({
getInitialState: function() {
return { items: [] }
},
render: function() {
var listItems = this.props.items.map(function(item) {
return (
<li key={item.name}>
<a className='button' href={item.link}>{item.name}</a>
</li>
);
});
return (
<div className='menu'>
<h3>The List</h3>
<ul>
{listItems}
</ul>
</div>
);
}
});
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('react-content'));
setTimeout(function() {
var newline = { name: 'Added item', link: 'https://amazon.com' };
items.push(newline);
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('react-content'));
}, 2000);
Much appreciated :)
React docs advise to place async calls in the componentDidMount method.
Load Initial Data via AJAX Fetch data in componentDidMount. When the
response arrives, store the data in state, triggering a render to
update your UI.
https://facebook.github.io/react/tips/initial-ajax.html
Here is a demo: http://codepen.io/PiotrBerebecki/pen/KgZGao
const App = React.createClass({
getInitialState: function() {
return {
items: [
{ name: 'Matt', link: 'https://google.com' },
{ name: 'Adam', link: 'https://bing.com' },
{ name: 'Luke', link: 'https://yahoo.com' },
{ name: 'John', link: 'https://apple.com' }
]
};
},
componentDidMount: function () {
setTimeout(() => {
this.setState({
items: [
...this.state.items,
{ name: 'Added item', link: 'https://amazon.com' }
]
});
}, 2000);
},
render: function() {
var listItems = this.state.items.map(function(item) {
return (
<RepeatModule key={item.name} href={item.link} itemName={item.name} />
);
});
return (
<div>
<h3>The List</h3>
{listItems}
</div>
);
}
});
const RepeatModule = React.createClass({
render: function() {
return (
<div className='menu'>
<ul>
<li>
<a className='button' href={this.props.href}>{this.props.itemName}</a>
</li>
</ul>
</div>
);
}
});
ReactDOM.render(
<App />,
document.getElementById('app')
);
Wrap the RepeatModule within a parent component. Items should be part of the state. Have a button to add new item. On click of the new item, pass the item details to the parent component. The parent component should update the state.
Your code won't work because you are pushing item to items. You should slice it before pushing it. React checks for props / state change using the === operator.
setTimeout(function() {
var newline = { name: 'Added item', link: 'https://amazon.com' };
var newItems = items.slice();
newItems.push(newline);
ReactDOM.render(<RepeatModule items={newItems} />, document.getElementById('react-content'));
}, 2000);
You could use the react state
working demo: http://codepen.io/anon/pen/WGyamK
var RepeatModule = React.createClass({
getInitialState: function() {
return { items: [
{ name: 'Matt', link: 'https://google.com' },
{ name: 'Adam', link: 'https://bing.com' },
{ name: 'Luke', link: 'https://yahoo.com' },
{ name: 'John', link: 'https://apple.com' }
] }
},
componentDidMount() {
var _this = this
setTimeout(function() {
var newline = { name: 'Added item', link: 'https://amazon.com' };
_this.setState({items: _this.state.items.concat(newline)});
}, 2000);
},
render: function() {
var listItems = this.state.items.map(function(item) {
return (
<li key={item.name}>
<a className='button' href={item.link}>{item.name}</a>
</li>
);
});
return (
<div className='menu'>
<h3>The List</h3>
<ul>
{listItems}
</ul>
</div>
);
}
});
ReactDOM.render(<RepeatModule />, document.getElementById('react-content'));
That being said, you should use some library trigerring the rerender for you. You are coming from Angular, so firstly you need to know that React is not a whole framework like angular, but just the "V in MVC".
I use react in combination with redux. Check out https://github.com/reactjs/redux for more.
There are some good boilerplate codes out there to get started quickly. I like this https://github.com/davezuko/react-redux-starter-kit
Hope you find this useful.

React: How to know which component calls the function

I am doing my first project using React and there is one thing I can't figure out. So I have many different Type components which are being set as the main component's TypesPage state. And when the onChange event happens on Type component I want to know which type it is in a TypesPage state or what index it is in a types array, so I can reupdate my data state.
Inside handleChange function I used jQuery's grep function comparing clicked Type title value with all the types array, but I am sure that is not the right way to do it and it would be an overkill with huge arrays.
Why I want to know which
handleChange:function(element, event){
var typeIndex;
$.grep(types, function(e, index){
if(element.title === e.title){
typeIndex = index
}
});
types[typeIndex] //Now I know that this is the Type that was changed
}
Fiddle
var types = [
{
type_id: 1,
type_name: "Logo"
},
{
type_id: 2,
type_name: "Ad"
},
{
type_id: 3,
type_name: "Catalog"
},
];
var Type = React.createClass({
render: function() {
return(
<li>
<input type="text" value={this.props.title}
onChange={this.props.handleChange.bind(null, this.props)} />
</li>
);
}
});
var TypesContainer = React.createClass({
render: function() {
var that = this;
return(
<ul>
{this.props.data.map(function(entry){
return(
<Type
key={entry.type_id}
title={entry.type_name}
handleChange={that.props.handleChange}
/>
);
})}
</ul>
);
}
});
var TypesPage = React.createClass({
getInitialState: function(){
return({data: types})
},
handleChange: function(element, event){
},
render: function() {
return(
<TypesContainer
data={this.state.data}
handleChange={this.handleChange}
/>
);
}
});
ReactDOM.render(
<TypesPage />,
document.getElementById('container')
);
I prefer ES6. The problem is, you have to bind your handleChange event with correct context of this and pass your arguments which you are expect to get inside your handle. See example below
class Example extends React.Component {
constructor(){
super();
this.state = {
data: [{id: 1, type: 'Hello'},{id: 2, type: 'World'},{id: 3, type: 'it"s me'}],
focusOn: null
};
}
change(index,e){
const oldData = this.state.data;
oldData[index].type = e.target.value;
this.setState({data:oldData, focusOn: index})
}
render(){
const list = this.state.data.map((item,index) =>
// this is the way how to get focused element
<input key={item.id} value={item.type} onChange={this.change.bind(this, index)}/>
);
return <div>
{list}
<p>Focused Element with index: {this.state.focusOn}</p>
</div>
}
}
React.render(<Example />, document.getElementById('container'));
fiddle
Thanks

Updating State, but not re-rendering in ReactJS?

I am very new to React and am just getting my feet wet. I'm having a hard time understand why this isn't re-rending the List. Here is my code:
app.jsx
var Hello = React.createClass({
getInitialState: function() {
return {
links: ['test ']
}
},
render: function() {
return <div className = "row">
<Submission linkStore = {this.state.links}/>
<List links = {this.state.links} />
</div>
}
});
var element = React.createElement(Hello, {});
ReactDOM.render(element, document.querySelector('.container'));
In my submission.jsx I have this function to push info into the links array
handleSubmitClick: function() {
this.props.linkStore.push(this.props.text)
this.setState({text: ''})
console.log(this.props.linkStore)
}
My list.jsx looks like this
module.exports = React.createClass({
getInitialState: function() {
return {
links: this.props.links
}
},
render: function() {
return <div>
{this.props.links}
</div>
}
});
Everything works as intended and I can get the test to show appropriately.
I am aware that this isn't going to show up as an actual list and that I should create a list component to show the items in list form. I'm just trying to run tests along the way to see how everything works.
Use parent state instead of child props.
try this
app.jsx
var Hello = React.createClass({
getInitialState: function() {
return {
links: ['test ']
}
},
handleListSubmitClick: function(params) {
this.setState({links:params});
},
render: function() {
return <div className = "row">
<Submission linkStore = {this.state.links} handleListSubmitClick={this.handleListSubmitClick}/>
<List links = {this.state.links} />
</div>
}
});
submission.jsx
handleSubmitClick: function() {
var linkStore = this.props.linkStore;
linkStore.push(this.props.text)
this.setState({text: ''})
this.props.handleListSubmitClick(linkStore);
}
but I don't understand this.props.text. input's value using this.refs.ref
list.jsx
module.exports = React.createClass({
getInitialState: function() {
return {
links: this.props.links
}
},
render: function() {
return <div>
{this.props.links}
</div>
}
});

Search in Backbone Collections, Updating UI with React

I'm spending time on something probably simple:
I'd like to implement a search bar, ideally updating the list of item as-you-type. My small app uses React and Backbone (for models and collections).
Displaying the list isn't too hard, it all works perfectly doing this (the mixin i'm using basically allows easy collections retrieval):
var List = React.createClass ({
mixins: [Backbone.React.Component.mixin],
searchFilter: function () {
//some filtering code here, not sure how (filter method is only for arrays...)
}
}
getInitialState: function () {
initialState = this.getCollection().map(function(model) {
return {
id: model.cid,
name: model.get('name'),
description: model.get('description')
}
});
return {
init: initialState,
items : []
}
},
componentWillMount: function () {
this.setState({items: this.state.init})
},
render: function(){
var list = this.state.items.map(function(obj){
return (
<div key={obj.id}>
<h2>{obj.name}</h2>
<p>{obj.description}</p>
</div>
)
});
return (
<div className='list'>
{list}
</div>
)
}
});
Now i've tried with no success to first translate the backbone collection into "state" with the getInitialState method, my idea was to proxy through a copy of the collection, which then could hold the search results. I'm not showing here my attemps for the sake of clarity(edit: yes i am), could someone guide me to the right approach? Thanks in advance.
There are many ways to accomplish this, but the simplest (in my opinion) is to store your search criteria in the List component's state and use it to filter which items from your collection get displayed. You can use a Backbone collection's built in filter method to do this.
var List = React.createClass ({
mixins: [Backbone.React.Component.mixin],
getInitialState: function () {
return {
nameFilter: ''
};
},
updateSearch: function (event) {
this.setState({
nameFilter: event.target.value
});
},
filterItems: function (item) {
// if we have no filter, pass through
if (!this.state.nameFilter) return true;
return item.name.toLowerCase().indexOf(this.state.nameFilter) > -1;
},
render: function(){
var list = this.props.collection
.filter(this.filterItems.bind(this))
.map(function(obj){
return (
<div key={obj.id}>
<h2>{obj.name}</h2>
</div>
)
});
return (
<div className='list'>
{list}
<input onChange={this.updateSearch} type="text" value={this.state.nameFilter}/>
</div>
)
}
});
var collection = new Backbone.Collection([
{
name: 'Bob'
},
{
name: 'Bill'
},
{
name: 'James'
}
]);
React.render(<List collection={collection}/>, document.body);
jsbin
The search criteria could easily be passed down from a parent component as a prop, so the search input does not have to live inside your List component.
Eventually I also found a different solution (below), but it involves copying the entire collection into state, which is probably not such a good idea...
var List = React.createClass ({
mixins: [Backbone.React.Component.mixin],
searchFilter: function () {
var updatedlist = this.state.init;
var searchText = this.refs.searchbar.getDOMNode().value
updatedlist = updatedlist.filter(function (item) {
return item.name.toLowerCase().search(
searchText.toLowerCase()) !== -1
});
this.setState({items: updatedlist})
}
},
getInitialState: function () {
initialState = this.getCollection().map(function(model) {
return {
id: model.cid,
name: model.get('name'),
description: model.get('description')
}
});
return {
init: initialState,
items : []
}
},
componentWillMount: function () {
this.setState({items: this.state.init})
},
render: function(){
var list = this.state.items.map(function(obj){
return (
<div key={obj.id}>
<h2>{obj.name}</h2>
<p>{obj.description}</p>
</div>
)
});
return (
<div className='list'>
<input ref='searchbar' type="text" placeholder="Search" onChange={this.searchFilter}/>
{list}
</div>
)
}
});

React: What happens on consecutive renders where a list shuffles?

I am trying to create a list which shuffles randomly with some animation.
Here is the fiddle for it where I have used key prop to identify each child.
http://jsfiddle.net/1wcpLLg4/
var ListAnimate = React.createClass({
list: [
{id: 1, caption: "Hello"},
{id: 2, caption: "There"},
{id: 3, caption: "Whatsup"},
{id: 4, caption: "Sanket"},
{id: 5, caption: "Sahu"},
],
shuffle: function() {
this.list.shuffle(); // Shuffles array!
this.forceUpdate();
},
render: function() {
return <div>
<button onClick={this.shuffle}>Shuffle</button>
<ul>
{this.list.map(function(el, i){
return <li key={el.id} style={ {top: (i*60)+'px'} }>{el.caption} {el.id}</li>;
})}
</ul>
</div>;
}
});
React.render(<ListAnimate />, document.body);
From the React docs about Dynamic Children, it states that key prop is used in order to identify the elements in an array during successive renders. So, that the items which are just re-ordered must not unmount and mount to new position rather they should just be re-positioned but the does not seem to be happening in the fiddle, where the Nodes at the top of the list are always being unmounted and being mounted at a different position.
But for the elements at the bottom seems to be working well with animation.
Keep in mind that for the kind of animation you are looking for, you need to always keep the DOM in the same order and only update their position in the render function of your component.
I modified your first fiddle using this strategy: http://jsfiddle.net/0maphg47/1/
render: function() {
// create a sorted version of the list to render the DOM
var sortedCopy = this.state.list.slice().sort(function(a, b) {
return a.id - b.id;
});
return <div>
<button onClick={this.shuffle}>Shuffle</button>
<ul>
{sortedCopy.map(function(el, i) {
// find the position of the element in the shuffled list
// which gives the position the element must be
var pos = this.state.list.indexOf(el);
return <li key={el.id} style={ {top: (pos*60)+'px'} }>
{el.caption} {el.id}
</li>;
}, this)}
</ul>
</div>;
}
There is still room for improvement but I'll leave that up to you.
I have created a fiddle to show that li elements are not actually being mounted / unmounted.
http://jsfiddle.net/jq9p7hnd/
I have converted li element to MyLi element and logged messages when the componentDidMount and componentWillUnmount functions are called. Only the componentDidMount callbacks are called during the first render and none of them are called after shuffle:
var MyLi = React.createClass({
componentDidMount : function(){
console.log("MyLi component did mount.");
},
componentWillUnmount : function(){
console.log("MyLi component will unmount.");
},
render : function(){
return <li {...this.props}>{this.props.children}</li>
}
});
var ListAnimate = React.createClass({
list: [
{id: 1, caption: "Hello"},
{id: 2, caption: "There"},
{id: 3, caption: "Whatsup"},
{id: 4, caption: "Sanket"},
{id: 5, caption: "Sahu"},
],
shuffle: function() {
this.list.shuffle();
this.forceUpdate();
},
render: function() {
return <div>
<button onClick={this.shuffle}>Shuffle</button>
<ul>
{this.list.map(function(el, i){
return <MyLi key={el.id} style={ {top: (i*60)+'px'} }>{el.caption} {el.id}</MyLi>;
})}
</ul>
</div>;
}
});
window.React = React;
React.render(<ListAnimate />, document.body);
Array.prototype.shuffle = function() {
var i = this.length, j, temp;
if ( i == 0 ) return this;
while ( --i ) {
j = Math.floor( Math.random() * ( i + 1 ) );
temp = this[i];
this[i] = this[j];
this[j] = temp;
}
return this;
}

Categories

Resources