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.
Related
i have this part of code the map function did not show any element of the array, if i console.log the variable it shows me the elements but for some reasons i can't show the elements on the screen.
Code
function Solution({list}){
const data = list
console.log(data);
return(
<div>
{
data?.map((item) => {
return (
<div>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export default Solution;
const list = [
{
title: "Home"
},
{
title: "Service",
subItem: ["Clean", "Cook"]
},
{
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
Solution({list})
Please, just pass "list" link this.
<Solution list={list}/>
Hope will help you, Thanks)
Check this out
import React from 'react';
function Solution({list}){
const data = list
console.log(list);
return(
<div>
{
data?.map((item) => {
return (
<div key={item.id}>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export function App(props) {
const list = [
{
id:1,
title: "Home"
},
{
id:2,
title: "Service",
subItem: ["Clean", "Cook"]
},
{
id:3,
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
return (
<div className='App'>
<Solution list={list} />
</div>
);
}
// Log to console
console.log('Hello console')
Have a unique key prop for each element when you map an array and send list array as props to your Solution component
I am trying to make a component in React to recursively display the names in a data tree. I am not that familiar with React and am not sure what I can do in my code to remove the error I get in the console.
The error is Uncaught SyntaxError: embedded: Adjacent JSX elements must be wrapped in an enclosing tag
Here is a jsFiddle to my code: https://jsfiddle.net/go79b0dp/
Here is my example code:
var treeObj = [
{
id: 1,
name: 'Bob',
children: [
{
id: 2,
name: 'Mary',
children: [
{id: 4, name: 'Suzy'}
]
},
{
id: 3,
name: 'Phil',
children: [
{id: 5, name: 'Jon'},
{id: 6, name: 'Paul'}
]
}
]
}
];
var TreeView = React.createClass({
getInitialState: function() {
return {
people: treeObj
};
},
render: function() {
var people = this.state.people;
var nodes = people.map((i) => <TreeNode node={i} children= {i.children} />)
return (
<ul>{nodes}</ul>
);
}
});
var TreeNode = React.createClass({
render: function() {
var nodes;
if (this.props.children) {
nodes = this.props.children.map((i) => <TreeNode node={i} children={i.children} />);
}
return (
<li>{this.props.node.name}</li>
<ul>{nodes}</ul>
);
}
});
ReactDOM.render(<TreeView />, document.getElementById('container'));
Your TreeNode component returns two sibling components: <li> and <ul>. Basically, you're trying to return two things from the render function, and you can't do that.
Normally, the recommended solution is to wrap them both in another element. For example:
return (<div>
<li>{this.props.node.name}</li>
<ul>{nodes}</ul>
</div>);
However, for the tree structure you're trying to create, it would probably be better to put the <ul> inside the <li>. That would be:
return (<li>
{this.props.node.name}
<ul>{nodes}</ul>
</li>);
This is how nested lists are commonly done in HTML.
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
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>
)
}
});
Currently constructing an App in reactjs and hit a strange issue. JSX code is below (sorry it's a bit verbose):
var NavigationTab = React.createClass({
onClick: function() {
console.log('NT' + this.props.tab.index);
this.props.onTabClick(this.props.tab.index);
},
render: function(index){
return (
<li>
<a href="#" onClick={this.onClick} className='navigation-tab'> {this.props.tab.title} </a>
</li>
)
}
});
var NavigationPanel = React.createClass({
getInitialState: function() {
return {
};
},
onTabClick: function(tab) {
//console.log(i) ;
this.setState({active : tab});
this.props.showTab(tab);
},
render: function() {
var self = this;
var tabs = this.props.tabs.map(function(item, index){
item.index = index;
return <NavigationTab tab={item} onTabClick={self.onTabClick} />;
});
return (
<div id='navigation-panel' className='col-xs-2'>
<ul className='nav nav-pills nav-stacked'>
{tabs}
</ul>
</div>
);
}
});
var App = React.createClass({
getInitialState: function() {
return {
tabs: [
{title: 'test', ref: 'test', content: <div> test </div>},
{title: 'Dasboard', ref: 'dashboard', content: <div> home </div>},
{title: 'Settings', ref: 'settings', content: <div> settings </div>},
{title: 'Logout', ref: 'logout', content: <div> logout </div>}
],
activeTab: 0};
},
showTab : function(index) {
console.log('AP ' + index);
this.setState({activeTab : index});
},
render: function() {
console.log('AP ' + this.state.activeTab);
console.log('AP ' + this.state.tabs[this.state.activeTab].title);
return (
<div id="container">
<NavigationPanel showTab={this.showTab} tabs={this.state.tabs} />
<div id="content-body">
{this.state.tabs[this.state.activeTab].content} /* [1] */
</div>
</div>
);
}
});
What happens is after changing tabs, the first tab 'test' will no longer display.
If I change the line at /* [1] */ to {this.state.tabs[this.state.activeTab].ref} it works as expected.
Here is a fiddle
In older versions of React, you couldn't reuse component descriptors and needed to recreate them when rendering them multiple times. Your code works fine with React 0.11.