Render element grouped by property object reactjs - reactjs

I have json Array Like this:
{
area:1,
label: "element1"
},
{
area:3,
label: "element3"
},
{
area:1,
label: "element2"
},
{
area:2,
label: "element2_1"
}
I would render an element like:
Area 1 -
element1
element2
Area 2-
element2_1
Area 3 -
element3
I have done this for now, but now I don't know how to group for property "area"
this.state.areas.map(function(el, key){
return (
<div>
<span><strong>{el.label}</strong></span>
</div>
);
)}

If you make your labels into an Array, you can group and render them like this.
var Hello = React.createClass({
componentWillMount: function() {
this.state = {
area: [
{
area: 1,
label: ['element 1', 'element 2']
},
{
area: 2,
label: ['element 2_1']
},
{
area: 3,
label: ['element 3']
}
]
};
},
render: function() {
return (
<div>
{ this.state.area.map(function(el, key) {
return (
<div key={el.area}>
<span>
<strong>
{ el.label.map(function(label) {
return (
<span key={label}>{label}</span>
)
}) }
</strong>
</span>
</div>
);
}) }
</div>
)
}
});
ReactDOM.render(
<Hello /> ,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>

I guess this can help you https://jsfiddle.net/69z2wepo/69759/
const myState = [{
area:1,
label: "element1"
},
{
area:3,
label: "element3"
},
{
area:1,
label: "element2"
},
{
area:2,
label: "element2_1"
}];
const ShowArea = (props) => <div><strong>Area {props.area}</strong><span>{props.children}</span></div>
const ShowLabel = (props) =>{
return(<span style={{paddingLeft:'10px'}} key={props.label}><strong>{props.label}</strong></span>)
};
var Hello = React.createClass({
render: function() {
const CompareByArea = (a,b) => a.area > b.area;
const OrderList = myState.sort(CompareByArea);
let newState = {};
OrderList.forEach(element=>{
if(!newState[element.area])
newState[element.area] = [];
newState[element.area].push({label: element.label});
});
const keys = Object.keys(newState);
const Elements = keys.map((key) => <ShowArea key={key} area={key} >
{newState[key].map(ShowLabel)}
</ShowArea>);
return (<div>{Elements}</div>)
}
});
ReactDOM.render(
<Hello/>,document.getElementById('container')
);

Related

Filtering two different arrays and one of it is nested

when user come to my website, I want him to choose group by clicking proper button, then filter all products by checking if in groups products_bound is the matching item. It's not working... I think I missed something in showProduct function, Im new and still learning please tell me if I'm doing something in bad way or what could be better in my code.
My dummy state in file:
products = [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
{
id: 3,
title: Carrot,
price: 1.99,
},
];
groups = [
{
name: Fruits,
products_bound: [
{
id: 1,
title: Apple,
price: 3.99,
},
{
id: 2,
title: Banana,
price: 5.99,
},
],
},
];
ChooseGroup.js
function ChooseGroup({ groups, showProductsButton }) {
return (
<div>
{groups.map((group) => {
return (
<button
id={group.name}
onClick={(e) => {
showProductsButton(e.target.id);
}}
>
{group.name}
</button>
);
})}
</div>
);
}
export default ChooseGroup;
index.js
function Homepage(props) {
const [filteredProductsByGroup, setFilteredProductsByGroup] = useState();
const [isThisGroupActive, setThisGroupActive] = useState(false);
function showProducts(clickedButtonId) {
const checkWhichGroupIsClicked = groups.filter(
(single) => single.name === clickedButtonId
);
const unpackGroup = checkWhichGroupIsClicked.map((single) =>
single.products_bound.map((i) => i)
);
const filter = products.filter((element) => {
unpackGroup.some((group) => {
return group == element.title;
});
});
return setFilteredProductsByGroup(filter), setThisGroupActive(true);
}
return (
<>
<Header />
<ChooseGroup groupList={props.groups} showProductsButton={showProducts} />
{isThisGroupActive ? (
filteredProductsByGroup.map((group) => {
return <Products groupDetails={group} />;
})
) : (
<div>Select group!</div>
)}
</>
);
}

How I can print nested array under value (object element) in React.js

I am trying to print a nested array (sublist) under object element (value) from the state.list. I tried but did not get the expected result I want. I made two components named Orderlist and Item which hold the nested array and value elements. I could not find where I am doing wrong. Sorry! I am in the learning stage and working on a super small project. Every help would be appreciated.
import React from "react";
import "./styles.css";
const Item = (props) => {
return props.list.map((item)=><li>{item.sublist}</li>);
};
const Orderlist = (props) => {
return props.list.map((element) => (
<ol>
{element.value}
<Item list = {props.list} />
</ol>
));
};
class App extends React.Component {
state = {
list: [
{ value: "Fruit", sublist: ["Banana", "Apple", "Graps"] },
{ value: "Vegetable", sublist: ["Carrat", "Potato", "Mushroom"] },
{ value: "Sports", sublist: ["Cricket", "Badminton", "Football"] },
{ value: "Continent", sublist: ["Asia", "Europe", "Africa"] }
]
};
render() {
return <Orderlist list={this.state.list} />;
}
}
export default App;
outcome got ........
Fruit
BananaAppleGraps
CarratPotatoMushroom
CricketBadmintonFootball
AsiaEuropeAfrica
Vegetable
BananaAppleGraps
CarratPotatoMushroom
CricketBadmintonFootball
AsiaEuropeAfrica
Sports
BananaAppleGraps
CarratPotatoMushroom
CricketBadmintonFootball
AsiaEuropeAfrica
Continent
BananaAppleGraps
CarratPotatoMushroom
CricketBadmintonFootball
AsiaEuropeAfrica
There were two mistakes:
You need to pass element.sublist as list prop to Item instead of props.list:
<Item list={element.sublist} />
List item should be just item instead of item.sublist:
<li>{item}</li>
const Item = (props) => {
return props.list.map((item, index) => <li key={index}>{item}</li>);
};
const Orderlist = (props) => {
return props.list.map((element, idx) => (
<ol key={idx}>
{element.value}
<Item list={element.sublist} />
</ol>
));
};
class App extends React.Component {
state = {
list: [
{ value: "Fruit", sublist: ["Banana", "Apple", "Graps"] },
{ value: "Vegetable", sublist: ["Carrat", "Potato", "Mushroom"] },
{ value: "Sports", sublist: ["Cricket", "Badminton", "Football"] },
{ value: "Continent", sublist: ["Asia", "Europe", "Africa"] }
]
};
render() {
return <Orderlist list={this.state.list} />;
}
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
https://codesandbox.io/s/dazzling-golick-0ie3qy?file=/src/App.js
{list.map((item, index) => (
<>
<li>{item.value}</li>
{item?.sublist?.map((subitem, index) => (
<>
<li>{subitem}</li>
</>
))}
</>
))}
try this :
const Item = (props) => {
return props.list.map((item)=><li>{item}</li>);
};
const Orderlist = (props) => {
return props.list.map((element) => (
<ol>
{element.value}
<Item list = {element.sublist} />
</ol>
))

Using increment to skip an iteration in React

I am using an increment (count) for not to click the period (.) second time. So once the period is clicked then second time it skips. I used the example from Incrementing state value by one using React, but the count is not incrementing.
const InitVal = ({ strValue, handleClick }) => (
<div>
{strValue.map((item) => (
<button onClick={() => handleClick(item.key)}>{item.key}</button>
))}
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {strValue: [{ key: '7' },{ key: '8' },{ key: '9' },{ key: '4' },{ key: '5' },{ key: '6' },{ key: '1' },{ key: '2' },{ key: '3' },{ key: '0' },{key: '.'}],value: '0',count: 0,};
this.handleClick = this.handleClick.bind(this);
}
handleClick(key) {
const { value } = this.state;
const { count } = this.state;
const digNprd = /[0-9.]/
if (value.charAt(0) === "0") {
this.setState({ value: `${key}` })
} else if (digNprd.test(key)) {
this.setState((u) => {
if (key === '.') {
if (u.count < 1) {
count: u.count + 1
} else {
key = ''
}
}
return { value: `${value}${key}` }
})
}
}
render() {
return (
<div><br /><InitVal strValue={this.state.strValue} handleClick={this.handleClick} /> <br /> <div>value: {this.state.value}</div><br />
<div>count: {this.state.count}</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id='root'></div>
Based on the code available in OP i am updating a working snippet for you as i am not sure why the updated solution is not working for you. With the help of this you can compare and find out where the issue lies.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.24.0/babel.js"></script>
<script type="text/babel">
const InitVal = ({ strValue, handleClick }) => (
<div>
{strValue.map((item) => (
<button onClick={() => handleClick(item.key)}>
{item.key}
</button>
))}
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
strValue: [
{ key: "7" },
{ key: "8" },
{ key: "9" },
{ key: "4" },
{ key: "5" },
{ key: "6" },
{ key: "1" },
{ key: "2" },
{ key: "3" },
{ key: "0" },
{ key: "." },
],
value: "0",
count: 0,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(key) {
const { count, value } = this.state;
const digNprd = /[0-9.]/;
if (value.charAt(0) === "0") {
this.setState((u) => {
let count = u.count
if (key === '.') {
if (count < 1) {
count = count + 1
} else {
key = ''
}
}
return { value: `${key}`, count }
});
} else if (digNprd.test(key)) {
this.setState((u) => {
let count = u.count;
if (key === ".") {
if (u.count < 1) {
count= u.count + 1;
} else {
key = "";
}
}
return { value: `${value}${key}`, count };
});
}
}
render() {
return (
<div>
<br />
<InitVal
strValue={this.state.strValue}
handleClick={this.handleClick}
/>{" "}
<br />{" "}
<div>value: {this.state.value}</div>
<br />
<div>count: {this.state.count}</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
</script>
</body>
</html>
For explanation you can refer to joseph's answer and my comment on that answer.
You are returning the value, and the key but you never return the new count value. So the state is not updating that value
try this:
handleClick(key) {
const { value } = this.state;
let { count } = this.state;
const digNprd = /[0-9.]/
if (value.charAt(0) === "0") {
this.setState({ value: `${key}` })
} else if (digNprd.test(key)) {
this.setState((u) => {
if (key === '.') {
if (u.count < 1) {
count = u.count + 1;
} else {
key = "";
}
}
return { value: `${value}${key}`, count }
})
}
}

React apply function to single item in array

The following code creates a simple movie rating app. Everything works except that when an up or down vote is clicked in one of the array items, the votes state for all items in the array update, rather than just for the item that was clicked. How do I code this so that the vote only applies to the item where it was clicked?
class Ratings extends React.Component {
constructor(props){
super(props);
this.state = {
votes: 0
};
this.add = this.add.bind(this);
this.subtract = this.subtract.bind(this);
this.reset = this.reset.bind(this);
}
add(event){
this.setState ({
votes: this.state.votes + 1
})
}
subtract(event){
this.setState ({
votes: this.state.votes - 1
})
}
reset(event){
this.setState ({
votes: 0
})
}
render () {
this.movies = this.props.list.map(x => {
return (
<div key={x.id} className="movierater">
<MoviePoster poster={x.img}/>
<h1 className="title">{x.name}</h1>
<div className="votewrapper">
<button onClick={this.add}><i className="votebutton fa fa-thumbs-o-up" aria-hidden="true"></i></button>
<Votes count={this.state.votes} />
<button onClick={this.subtract}><i className="votebutton fa fa-thumbs-o-down" aria-hidden="true"></i></button>
</div>
<button onClick={this.reset} className="reset">Reset</button>
</div>
)
});
return (
<div>
{this.movies}
</div>
);
}
}
function MoviePoster(props) {
return (
<img src={props.poster} alt="Movie Poster" className="poster"/>
);
}
function Votes(props) {
return (
<h2>Votes: {props.count}</h2>
);
}
var movieposters = [
{id: 1, img:"http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg", name: "Alien Covenant"},
{id: 2, img:"http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg", name: "Atomic Blonde"},
{id: 3, img:"http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg", name: "Easy Living"},
{id: 4, img:"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg", name: "Once Upon a Time in Venice"},
{id: 5, img:"http://www.impawards.com/2017/posters/med_scorched_earth.jpg", name: "Scorched Earth"},
{id: 6, img:"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg", name: "Underworld: Blood Wars"},
{id: 7, img:"http://www.impawards.com/2017/posters/med_void.jpg", name: "The Void"},
{id: 8, img:"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg", name: "War for the Planet of the Apes"},
]
ReactDOM.render(
<Ratings list={movieposters} />,
document.getElementById('app')
);
You need a separate vote count for each movie entity.
This can be accomplished by providing an id to each movie and setting the vote for that specific movie by id.
I would also recommend to extract a new component for a Movie.
this component will get the movieId as a prop and the handlers, it will invoke the up or down handlers and provide to them the current movieId.
See a running example:
class Movie extends React.Component {
onSubtract = () => {
const { subtract, movieId } = this.props;
subtract(movieId);
};
onAdd = () => {
const { add, movieId } = this.props;
add(movieId);
};
onReset = () => {
const { reset, movieId } = this.props;
reset(movieId);
};
render() {
const { movie, votes = 0 } = this.props;
return (
<div className="movierater">
<MoviePoster poster={movie.img} />
<h1 className="title">{movie.name}</h1>
<div className="votewrapper">
<button onClick={this.onAdd}>
<i className="votebutton fa fa-thumbs-o-up" aria-hidden="true" />
</button>
<Votes count={votes} />
<button onClick={this.onSubtract}>
<i className="votebutton fa fa-thumbs-o-down" aria-hidden="true" />
</button>
</div>
<button onClick={this.onReset} className="reset">
Reset
</button>
</div>
);
}
}
class Ratings extends React.Component {
constructor(props) {
super(props);
this.state = {
allVotes: {}
};
}
subtract = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote - 1
};
this.setState({allVotes: nextState});
};
add = movieId => {
const { allVotes } = this.state;
const currentVote = allVotes[movieId] || 0;
const nextState = {
...allVotes,
[movieId]: currentVote + 1
};
this.setState({ allVotes: nextState });
};
reset = movieId => {
const { allVotes } = this.state;
const nextState = {
...allVotes,
[movieId]: 0
};
this.setState({ allVotes: nextState });
};
render() {
const { allVotes } = this.state;
this.movies = this.props.list.map(x => {
const votes = allVotes[x.id];
return (
<Movie
movieId={x.id}
movie={x}
votes={votes}
reset={this.reset}
subtract={this.subtract}
add={this.add}
/>
);
});
return <div>{this.movies}</div>;
}
}
function MoviePoster(props) {
return <img src={props.poster} alt="Movie Poster" className="poster" />;
}
function Votes(props) {
return <h2>Votes: {props.count}</h2>;
}
var movieposters = [
{
id: 1,
img: "http://www.impawards.com/2017/posters/med_alien_covenant_ver4.jpg",
name: "Alien Covenant"
},
{
id: 2,
img: "http://www.impawards.com/2017/posters/med_atomic_blonde_ver4.jpg",
name: "Atomic Blonde"
},
{
id: 3,
img: "http://www.impawards.com/2017/posters/med_easy_living_ver3.jpg",
name: "Easy Living"
},
{
id: 4,
img:
"http://www.impawards.com/2017/posters/med_once_upon_a_time_in_venice_ver3.jpg",
name: "Once Upon a Time in Venice"
},
{
id: 5,
img: "http://www.impawards.com/2017/posters/med_scorched_earth.jpg",
name: "Scorched Earth"
},
{
id: 6,
img:
"http://www.impawards.com/2017/posters/med_underworld_blood_wars_ver9.jpg",
name: "Underworld: Blood Wars"
},
{
id: 7,
img: "http://www.impawards.com/2017/posters/med_void.jpg",
name: "The Void"
},
{
id: 8,
img:
"http://www.impawards.com/2017/posters/med_war_for_the_planet_of_the_apes.jpg",
name: "War for the Planet of the Apes"
}
];
ReactDOM.render(<Ratings list={movieposters} />, document.getElementById("root"));
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You need to keep track of votes for each element, thus this.state.votes +/- 1 doesn't do the job, so:
Change
this.state = {
votes: 0
}
to
this.state = {
votes: {}
}
then change the functions:
add(id){
return function(event) {
this.setState ({ ...this.state.votes, [id]: parseInt(this.state.votes[id]) + 1 })
}
}
and the same for subtract. Then change your buttons to:
<button onClick={this.add(x.id)} ... (same for subtract)
and last change your Vote component:
<Votes count={this.state.votes[x.id] || 0} />
On reset just do:
reset(event){
this.setState ({ votes: {} })
}

React add class active on selected tab

I have the following:
var Tab = React.createClass({
getInitialState: function(){
return {
selected:''
}
},
activateTab: function(e) {
e.preventDefault();
$('.navigation--active').removeClass('navigation--active');
this.setState({selected : true});
},
render: function() {
var isActive = this.state.selected === true ? 'navigation--active': '';
return (
<li onClick={this.activateTab} className={isActive}>
<p>
{this.props.content}
</p>
</li>
);
}
});
var Tabs = React.createClass({
render: function() {
var tabs = [],
total = this.props.data.points.total,
handleClick = this.handleClick;
total.forEach(function(el, i){
tabs.push(
<Tab content = {el.name}
key = {i}/>
);
});
return (
<ul className="navigation">
{tabs}
</ul>
);
}
});
however it only works when you click once on every tab, if you click the second time on the same tab the class doesn't get added anymore
In this case, would be better move state management to parent component Tabs, and pass to child only props which you need to detect class name or set new state in parent
var Tab = React.createClass({
render: function() {
return <li
className={ this.props.isActive ? 'navigation--active': '' }
onClick={ this.props.onActiveTab }
>
<p>{ this.props.content }</p>
</li>
}
});
var Tabs = React.createClass({
getInitialState: function() {
return { selectedTabId: 1 }
},
isActive: function (id) {
return this.state.selectedTabId === id;
},
setActiveTab: function (selectedTabId) {
this.setState({ selectedTabId });
},
render: function() {
var total = this.props.data.points.total,
tabs = total.map(function (el, i) {
return <Tab
key={ i }
content={ el.name }
isActive={ this.isActive(el.id) }
onActiveTab={ this.setActiveTab.bind(this, el.id) }
/>
}, this);
return <ul className="navigation">
{ tabs }
</ul>
}
});
const data = {
points: {
total: [
{ id: 1, name: 'tab-1', text: 'text' },
{ id: 2, name: 'tab-2', text: 'text-2' },
{ id: 3, name: 'tab-3', text: 'text-2' }
]
}
}
ReactDOM.render(
<Tabs data={ data } />,
document.getElementById('container')
);
.navigation {}
.navigation--active {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
Necro poster here.
Cool answer up above!
In any way, here is the 2018 upgrade answer with recompose and styled-components. You can even make a HOC out of it for a joyful reusability!
https://codesandbox.io/s/1826454zl7
import React from "react";
import ReactDOM from "react-dom";
import { compose, withState, withHandlers } from "recompose";
import styled from "styled-components";
const enhancer = compose(
withState("selectedTabId", "setSelectedTabId", 1),
withHandlers({
isActive: props => id => {
return props.selectedTabId === id;
},
setActiveTab: props => id => {
props.setSelectedTabId(id);
}
})
);
const Tabs = enhancer(props => {
return (
<ul>
{props.data.map((el, i) => {
return (
<Tab
key={i}
content={el.name}
isActive={props.isActive(el.id)}
onActiveTab={() => props.setActiveTab(el.id)}
/>
);
})}
</ul>
);
});
const Tab = props => {
return (
<StyledLi isActive={props.isActive} onClick={props.onActiveTab}>
<p>{props.content}</p>
</StyledLi>
);
};
const StyledLi = styled.li`
font-weight: ${({ isActive }) => (isActive ? 600 : 100)};
cursor: pointer;
font-family: Helvetica;
transition: 200ms all linear;
`;
const data = [
{ id: 1, name: "tab-1", text: "text" },
{ id: 2, name: "tab-2", text: "text-2" },
{ id: 3, name: "tab-3", text: "text-2" }
];
const ExampleApp = () => <Tabs data={data} />;
ReactDOM.render(<ExampleApp />, document.getElementById("app"));
Basic idea is that you need to get selected index, map over the item on every click, compare selected index with all other indexes and return true to props of a needed component if the match is found.

Resources