reactjs create dynamic wrapper element in map() - loops

i have issues to probably understand ReactJS well enough to create dynamic div wrapper in every five steps. To be more specific here is an example:
render() {
return (
<div className='holder'>
{this.props.elements.map(
(b,n) =>
{n%5 == 0 ? '<div class="grid grid-pad">' : ''}
<Component param={b} />
{n%5 == 0 ? '</div>' : ''}
)}
</div>
)
}
The results should look like this:
<div class='grid grid-pad'>
<div class='child'></div>
<div class='child'></div>
<div class='child'></div>
<div class='child'></div>
<div class='child'></div>
</div>
<div class='grid grid-pad'>
<div class='child'></div>
<div class='child'></div>
...
</div>
....
So the result would be that every 5 elements would be wrapped in div.
I am aware that this is not the right way, at this code actually it produces errors for not closed tags. Is there any way how to actually achieve similar functionality. Thank you in advance.

As you can't dynamically create a React component from a string quite like that. You'll just need to group the children manually and add them as a whole to a container within the render method.
Here's an example: http://jsfiddle.net/wiredprairie/ko8so1mu/.
If you were using something like lodash, you could reduce the number of lines of code below by using the take function.
render() {
// make all elements into Components
var elements = this.props.elements || [];
var components = elements.map(
(b) => {
return <Component param={b} />;
}
);
// then just group into chunks of 5
var groups = [];
var children = [];
while(components.length > 0) {
children.push(components.shift());
if (children.length === 5) {
groups.push(<div className="grid grid-pad">{children}</div>);
children = [];
}
}
// remaining children
if (children.length > 0 ) {
groups.push(<div className="grid grid-pad">{children}</div>);
}
return (
<div className='holder'>
{ groups }
</div>
);
}

This is how you could do it:
/** #jsx React.DOM */
var Parent = React.createClass({
getInitialState: function() {
return {
list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
}
},
render: function() {
return(
<div>
<Child elements={this.state.list} />
</div>
);
}
});
var Child = React.createClass({
render: function() {
var itemsList = this.props.elements.map(function(element, index) {
if (index % 5 == 0) {
return (
<div> {element} </div>
);
}
});
return(
<div>
{itemsList}
</div>
);
}
});
React.render(<Parent />, document.getElementById('app'));
You keep the data in the Parent component then pass it to the Child component in props.
if you do console.log(itemsList) you can see what is happening inside map() method.
Here is JSFiddle.
Edit:
After you updated your question, here is something I quickly hacked, this is not the best way of doing it, but it works:
/** #jsx React.DOM */
var Parent = React.createClass({
getInitialState: function() {
return {
list: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
}
},
render: function() {
return(
<div>
<Child elements={this.state.list} />
</div>
);
}
});
var Child = React.createClass({
render: function() {
var arr_index = -1;
var itemsList = [];
var items = this.props.elements.map(function(item, i) {
if (i % 5 == 0) {
arr_index += 1
}
itemsList.push(<div> {item}, conatiner: {arr_index} </div>)
});
return (
<div>{itemsList}</div>
);
}
});
React.render(<Parent />, document.getElementById('app'));
You still need to tweak it a bit to meet your requirements, but basically it divides children into "containers", I guess that's what you were looking for.

Related

How to .map render list of items grouped into category in React

Hello guys i wanted to render some items into their own category using .map, but i dont know how to do it . I only know how to render standard list. Below is the example of my data:
[
{
"id_kategoriTest": 1,
"kategori": "Hematologi",
"id_jenisTest": 24,
"jenis": "Coombs Test Direct"
},
{
"id_kategoriTest": 1,
"kategori": "Hematologi",
"id_jenisTest": 25,
"jenis": "Hb Elektroforesis"
},
{
"id_kategoriTest": 14,
"kategori": "Imuno-Serologi",
"id_jenisTest": 247,
"jenis": "Anti HBs Titer"
},
{
"id_kategoriTest": 14,
"kategori": "Imuno-Serologi",
"id_jenisTest": 248,
"jenis": "Anti HBc"
}
]
The output i wanted is like this:
Hematologi
Coombs Test Direct
Hb Elektroforesis
Imuno-Serology
Anti HBs Titer
Anti HBc
Thanks.
First step is to sort your array to something more convenient.
Then render the sorted array.
Here, myArray contain your array above
const sorterAR = [];
myArray.forEach((item) => {
let cat = sorterAR.find(
(cat) => cat.id_kategoriTest === item.id_kategoriTest
);
if (!cat) {
cat = {
id_kategoriTest: item.id_kategoriTest,
kategori: item.kategori,
items: [],
};
sorterAR.push(cat);
}
cat.items.push(item);
});
return (
<div className="App">
{sorterAR.map((cat) => (
<>
<div>
<b>{cat.kategori}</b>
</div>
<ul>
{cat.items.map((item) => (
<li>{item.jenis}</li>
))}
</ul>
</>
))}
</div>
);

How to specify the order of items in ReactCSSTransitionGroup?

Is it possible to specify the order of items in a ReactCSSTransitionGroup?
Consider a list of items which their order is important. If you want to show one item and hide its adjacent with one action, ReactCSSTransitionGroup confuses their order. Take a look the following fiddle, the items are supposed to be [1, 2, 3, 4, 5].
http://jsfiddle.net/mehranziadloo/kb3gN/15519/
Is it possible to tell ReactCSSTransitionGroup (or ReactTransitionGroup) the sequence of items?
The ReactCSSTransitionGroup just animates changes in DOM, it doesn't care about order. Your state changes from odd to even numbers, there is no moment when it contains sorted array with all the numbers. You can work around it by modifying state in different way, temporary saving old items for animation purposes, something like that:
switch: function() {
var newItems;
if (this.state.items[0] % 2 !== 1) {
newItems = [1, 3, 5];
}
else {
newItems = [2, 4];
}
this.setState({
items: newItems,
previousItems: this.state.items
}, function() {
this.setState({
previousItems: []
})
});
}
After that, you need to modify your render method:
render: function() {
var currentItems = this.state.items.concat(this.state.previousItems).sort();
var items = currentItems.map(function(item, i) {
return (
<div key={item}>
{item}
</div>
);
}.bind(this));
return (
<div>
<button onClick={this.switch}>Switch</button>
<ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={1000} transitionLeaveTimeout={1000}>
{items}
</ReactCSSTransitionGroup>
</div>
);
}
Here is an updated fiddle: http://jsfiddle.net/ny5La5ky/

React onClick fires multiple times on load and doesn't contain callback function in component props

I believe I have two basic problems, which are probably connected. I'm trying to place an event handler with a callback function on a nested component. It didn't seem to be doing anything, so I replaced the callback function with an alert of JSON.stringify(this.props) to see if that would shed any light. It illuminated two problems: 1) my callback function was not in the props. 2) the alert popped up 2 times on page load, but did not pop up on click, like it was supposed to. I'm working through this React tutorial. Here are the relevant components:
var App = React.createClass({
mixins: [Catalyst.LinkedStateMixin],
getInitialState: function(){
return {
fishes: {},
order: {}
}
},
componentDidMount: function(){
base.syncState(this.props.params.storeId + '/fishes', {
context: this,
state: 'fishes'
});
var localStorageRef = localStorage.getItem('order-' + this.props.params.storeId);
if(localStorageRef){
this.setState({
order: JSON.parse(localStorageRef)
});
}
},
componentWillUpdate: function(nextProps, nextState){
localStorage.setItem('order-' + this.props.params.storeId, JSON.stringify(nextState.order));
},
loadSamples: function(){
this.setState({
fishes: require('./sample-fishes.js')
});
},
addFish: function(fish){
var timestamp = (new Date()).getTime();
this.state.fishes['fish-' + timestamp] = fish;
this.setState({ fishes: this.state.fishes });
},
removeFish: function(key){
if(confirm("Are you sure you want to remove this fish?")){
this.state.fishes[key] = null;
this.setState({ fishes: this.state.fishes });
}
},
addToOrder: function(key){
this.state.order[key] = this.state.order[key] + 1 || 1;
this.setState({ order: this.state.order });
},
// <<<<<<<< the function I'm having trouble with >>>>>>>>
removeFromOrder: function(key){
alert('hi');
delete this.state.order[key];
this.setState({ order: this.state.order });
},
renderFish(key){
return <Fish key={key} index={key} details={this.state.fishes[key]} addToOrder={this.addToOrder}/>
},
render: function(){
return (
<div className="catch-of-the-day">
<div className="menu">
<Header tagline="Fresh Seafood Market"/>
<ul className="list-of-fish">
{/*{ Object.keys(this.state.fishes).map(this.renderFish) }*/}
{ Object.keys(this.state.fishes).length > 0 ? Object.keys(this.state.fishes).map(this.renderFish) : <li>No Fishes!</li> }
</ul>
</div>
// <<<<<<<< I pass the function through to the Order component >>>>>>>>
<Order fishes={this.state.fishes} order={this.state.order} removeFromOrder={this.removeFromOrder}/>
<Inventory fishes={this.state.fishes} addFish={this.addFish} removeFish={this.removeFish} loadSamples={this.loadSamples} linkState={this.linkState}/>
</div>
)
}
});
var Order = React.createClass({
renderOrder: function(key){
var fish = this.props.fishes[key];
var count = this.props.order[key];
// <<<<<<<< the onClick I'm having trouble with >>>>>>>>
var removeButton = <button onCLick={this.props.removeFromOrder.bind(null, key)}>×</button>
// var removeButton = <button onCLick={alert(JSON.stringify(this.props))}>×</button>
if(!fish) {
return <li key={key}>Sorry, that fish is no longer available! {removeButton}</li>
// return <li key={key}>Sorry, that fish is no longer available!</li>
}
return (
<li key={key}>
{count}lbs
{" " + fish.name}
<span className="price">{helpers.formatPrice(count * fish.price)} {removeButton}</span>
{/*<span className="price">{helpers.formatPrice(count * fish.price)}</span>*/}
</li>
)
},
render: function(){
var orderIds = Object.keys(this.props.order);
var total = orderIds.reduce((prevTotal, key)=>{
var fish = this.props.fishes[key];
var count = this.props.order[key];
var isAvailable = fish && fish.status === 'available';
if(isAvailable) {
return prevTotal + (count * parseInt(fish.price) || 0);
}
return prevTotal;
}, 0);
return (
<div className="order-wrap">
<h2 className="order-title">Your Order</h2>
<ul className="order">
{ orderIds.length > 0 ? orderIds.map(this.renderOrder) : ""}
<li className="total">
<strong>Total:</strong>
{helpers.formatPrice(total)}
</li>
</ul>
</div>
)
}
});
The props for Order should include: the available fishes with all of their details, the current order with a fish id and quantity, and the removeFromOrder callback. When I explore the component in React dev tools, it has all of these things.
When I replace the removeFromOrder callback with an alert of the props, what happens is:
- on click, nothing
- on page refresh, two alerts pop up: the props in the first include the current order and an empty fishes array, the props in the second include the current order and the populated fishes array. Neither show the removeFromOrder callback function, which appears to be undefined from the perspective of the event listener.
On a potentially related note, when I explore the component in React dev tools and hover over a list item in the Order, I get the following error: TypeError: node.getBoundingClientRect is not a function. I'm not sure if this is part of my problem; if it's not, I'm not too concerned about it, since it only seems to pop up when I hover over the element in dev tools.
Thank you for reading this long thing, and any help would be much appreciated!
As #azium pointed out, the problem was a simple typo: onCLick={alert()} should instead be onClick={() => alert()}. Facepalm.

Using for loop render some Html elements in React render function

I am trying to render some html using for loop. Every thing work fine but the html prints like a string inside that UL element I dont know what I did wrong pls help me with this. I am new to this React Js thing.
My Script
const Pagination = React.createClass({
getInitialState: function () {
return {
"page": {
"size": 100,
"limit": 30,
"offset": 0,
"number": 1
}
};
},
render: function () {
var html = [];
var i = 0;
var className = '';
var noOfPages = 0;
var number = 1;
var page = this.state.page;
if (page) {
noOfPages = parseInt((page.size / page.limit), 10);
for (i; i < noOfPages; i++) {
className = (className === number) ? 'selected' : '';
html.push('<li key=' + {i} + ' className=""><button className="' + {className} + '">' + {i} + '</button></li>');
}
}
return (
<div className="pagination-sec-wrapper">
<form className="pagination-inner-sec-wrapper" id="pagination-form">
<ul className="">
{html}
</ul>
</form>
</div>
);
}
});
That's because you are pushing string values into your HTML array.
html.push('<span key=' + {i} + ' className=""><button className="' + {className} + '">' + {i} + '</button></span>');
You need to push React components instead.
html.push(<span key={i} className="">
<button className={className}>{i}</button>
</span>);
It's important to remember that even though JSX looks like HTML strings, it's really just syntactic sugar for regular Javascript function calls.
The above JSX is just the same as the following JS.
html.push(React.createElement("span", { key: i, className: "" },
React.createElement("button", { className: className }, i)));

React, adding components in component

I started to learn React and I hit first wall.
I have a list component which should display a list of rows + button for adding a new row.
All is in those 2 gists:
https://gist.github.com/matiit/7b361dee3f878502e10a
https://gist.github.com/matiit/8bac28c4d5c6ce3993c7
The addRow method is executed on click, because I can see the console.log, but no InputRows are added.
Can't really see why.
This is a little updated (dirty) code which doesn't work either.
Now it's only one file:
var InputList = React.createClass({
getInitialState: function () {
return {
rowCount: 1
}
},
getClassNames: function () {
if (this.props.type === 'incomes') {
return 'col-md-4 ' + this.props.type;
} else if (this.props.type === 'expenses') {
return 'col-md-4 col-md-offset-1 ' + this.props.type;
}
},
addRow: function () {
this.state.rowCount = this.state.rowCount + 1;
this.render();
},
render: function () {
var inputs = [];
for (var i=0;i<this.state.rowCount; i++) {
inputs.push(i);
}
console.log(inputs);
return (
<div className={ this.getClassNames() }>
{inputs.map(function (result) {
return <InputRow key={result} />;
})}
<div className="row">
<button onClick={this.addRow} className="btn btn-success">Add more</button>
</div>
</div>
);
}
});
this.render() doesn't do anything. If you look at the function, it simply does some calculations and returns some data (the virtual dom nodes).
You should be using setState instead of directly modifying it. This is cleaner, and allows react to know something's changed.
addRow: function () {
this.setState({rowCount: this.state.rowCount + 1});
},
Don't store list of components in state.
Instead store the income row count and expense row count in state.
Use click handler to increment these counts.
Use render method to generate required rows based on count.

Resources