constructor(props) {
super(props);
this.state = {
posts: [],
loading: true
};
}
componentDidMount() {
axios.get('/posts')
.then(response => {
console.log('---');
console.log(response.data);
console.log('---');
this.setState({ posts: response.data, loading: false });
});
}
toggleItem(index) {
console.log('clicked index: '+index);
}
render () {
let content;
if (this.state.loading) {
content = 'Loading...';
} else {
content = this.state.posts.map(post => {
return(
<li key={post.id} className={}>
<div>
<Moment format="MMM DD # HH:MM">
<span className="badge badge-pill badge-primary">{post.created_at}</span>
</Moment>
<button className="btn btn-primary btn-sm" onClick={this.toggleItem.bind(this, post.id)}>Toggle Message</button>
</div>
<div className="msg">{post.message}</div>
</li>
)
});
}
return (
<div>
<h1>Posts!</h1>
<div className="row">
<div className="col-md-6">
{content}
</div>
<div className="col-md-6">
x
</div>
</div>
</div>
);
What I am trying to achieve - when someone clicks the button, I want to toggle (show or hide) the respective .msg.
Where I struggle - I would like to default hide all the messages and when the button is clicked, then to display the respective msg. But I am not sure how to do it in React - one thought is to hide them in default with using CSS and then to create a new state for the clicked item?
Or should I pre-create an array of states for monitoring all messages?
You can add CSS classes or styling conditionally based on a Boolean key of each item.
When an item gets clicked, just flip the Boolean value.
Here is a small running example:
class Item extends React.Component {
onClick = () => {
const { onClick, id } = this.props;
onClick(id);
}
render() {
const { name, active } = this.props;
return (
<div
onClick={this.onClick}
style={{ opacity: active ? '1' : '0.4' }}
>
{name}
</div>
);
}
}
class App extends React.Component {
state = {
items: [
{ name: 'Item 1', id: 1 },
{ name: 'Item 2', id: 2 },
{ name: 'Item 3', id: 3 },
{ name: 'Item 4', id: 4 },
{ name: 'Item 5', id: 5 },
]
}
onItemClick = id => {
this.setState(prev => {
const { items } = prev;
const nexItems = items.map(item => {
if (item.id !== id) return item;
return {
...item,
active: !item.active
}
});
return { ...prev, items: nexItems };
});
}
render() {
const { items } = this.state;
return (
<div>
{
items.map(item => (
<Item key={item.id} {...item} onClick={this.onItemClick} />
))
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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>
Related
My code below shows my current component design. This is a counter component which is responsible for incrementing a counter for the respective array item and also for adding the clicked item to the cart. I am trying to figure out if there is some way in which I can assign each array item within the items array to its own state count value. Currently, the screen shows four array items, with each one having a button next to it and also a count. When clicking the increment button for any particular item, the state count for all buttons is updated and rendered, which is not what I want. I have tried to assign each button it's own state count in several ways, but haven't been able to figure out the right way. I would like to somehow bind a state count value to each button so that each one has it's individual state count.I would really appreciate if someone can provide some tips or insight as I dont know of a way to isolate the state count for each button and make it unique so that when one value's button is clicked, only the state count for that particular button (located next to the increment button) is updated and not the others.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
cart: [],
};
}
handleIncrement = (e) => {
this.setState({
count: this.state.count + 1,
cart: [...this.state.cart, e.target.value],
});
};
render() {
const listItems = this.props.items.map((item) => (
<li key={item.id}>
{item.value}
<button onClick={this.handleIncrement}>+</button>
{this.state.count}
</li>
));
return (
<div>
{listItems}
</div>
);
}
}
What I did here is I remove the constructor, update Counter component props, update the event on how to update your cart in Example component, adjusted the Counter component, for the Cart component, I added componentDidMount and shouldComponentUpdate make sure that the component will re-render only when props listArray is changing. Here's the code.
class Example extends React.Component {
state = {
cart: [],
items: [
{ id: 1, value: "L1" },
{ id: 2, value: "L2" },
{ id: 3, value: "L3" },
{ id: 4, value: "L4" }
]
}
render() {
const { cart } = this.state
return (
<div>
<h1>List</h1>
{ items.map(
({ id, ...rest }) => (
<Counter
key={ id }
{ ...rest }
cart={ cart }
onAddToCard={ this.handleAddCart }
/>
)
) }
</div>
)
}
handleAddCart = (item) => {
this.setState(({ items }) => ([ ...items, item ]))
}
}
class Counter extends React.Component {
state = {
count: 0
}
handleIncrement = () => {
this.setState(({ count }) => ({ count: count++ }))
}
render() {
const { count } = this.state
const { cart, value } = this.props
return (
<div>
{ value }
<span>
<button onClick={ this.handleIncrement }>+</button>
{ count }
</span>
<Cart listArray={ cart } />
</div>
)
}
}
class Cart extends React.Component {
state = {
cart: []
}
addTo = () => (
<div>List: </div>
)
componentDidMount() {
const { cart } = this.props
this.setState({ cart })
}
shouldComponentUpdate({ listArray }) {
return listArray.length !== this.state.cart.length
}
render() {
return (
<div>
<ListFunctions addClick={ this.addTo } />
</div>
)
}
}
const ListFunctions = ({ addClick }) => (
<div>
<button onClick={ addClick }>Add To List</button>
</div>
)
If you want to add to the list of items without rendering the button, you can add a custom property to mark that it is a custom addition:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, value: "L1" },
{ id: 2, value: "L2" },
{ id: 3, value: "L3" },
{ id: 4, value: "L4" },
]
}
}
addToItems = items => {
this.setState({
items,
});
}
render() {
var cartArray = [];
return (
<div>
<h1>List</h1>
{this.state.items.map((item) =>
<Counter
key={item.id}
value={item.value}
id={item.id}
custom={item.custom}
cart={cartArray}
addToItems={this.addToItems}
items={this.state.items}
/>
)}
</div>
);
}
}
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
}
handleIncrement = () => {
this.setState({
count: this.state.count + 1,
});
this.props.cart.push(this.props.value);
};
addTo = () => {
const { items } = this.props;
let lastId = items.length;
lastId++;
this.props.addToItems([
...items,
{
id: lastId,
value: `L${lastId}`,
custom: true,
}]);
};
render() {
return (
<div>
{this.props.value}
{
!this.props.custom &&
(
<span>
<button onClick={this.handleIncrement}>+ </button>
{this.state.count}
</span>
)
}
<Cart addTo={this.addTo} />
</div>
);
}
}
class Cart extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<ListFunctions
addClick={this.props.addTo}
/>
</div>
);
return null;
}
}
const ListFunctions = ({ addClick}) => (
<div>
<button onClick={addClick}>Add To List</button>
</div>
);
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I want to increment the value on Items component on a button click which is handled by its child component Item. Items have an array of key-value pairs and this value needs to increment and render it
Here is the code
//parent component
class Items extends React.Component{
state={
items:[{ id: 1, value: 9 },
{ id: 2, value: 10 },
{ id: 3, value: 0 }]
}
handleIncrement=()=>{
//need to increment items.value on each button click increment. How can I access it
}
render(){
return(
<div>
<h2>Increment item on the list From Parent</h2>
{this.state.items.map(item=>(<Item key={item.id}
value={item.value} id={item.id} onIncrement={this.handleIncrement}
/>))}
</div>
)
}
}
//child component
class Item extends React.Component{
getValue=()=>{
let {value}=this.props;
return value===0?'Zero':value
}
render(){
return(
<div>
<span>{this.getValue()}</span>
<button onClick={this.props.onIncrement}>Increment</button>
</div>
)
}
}
please help me with this.
You might add id as button name
<button name={this.props.id} onClick={this.props.onIncrement}>Increment</button>
and then use it at your function
handleIncrement= e =>
this.setState({ items: this.state.items.map(item =>
item.id == e.target.name ? {...item, value: item.value++ } : item ) })
Or you can update by array index instead of object id
//parent component
class Items extends React.Component {
state = {
items: [{ id: 1, value: 9 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
};
handleIncrement = e => {
//need to increment items.value on each button click increment.How can i access it
const id = e.target.id;
const tempItems = this.state.items;
tempItems[id] = {...tempItems[id], value: ++tempItems[id].value}
this.setState((prevState)=>({ items: tempItems}));
};
render() {
return (
<div>
<h2>Increment item on the list From Parent</h2>
{this.state.items.map((item,i) => (
<Item
key={item.id}
value={item.value}
id={item.id}
index={i}
onIncrement={this.handleIncrement}
/>
))}
</div>
);
}
}
//child component
class Item extends React.Component {
getValue = () => {
let { value } = this.props;
return value === 0 ? "Zero" : value;
};
render() {
return (
<div>
<span>{this.getValue()}</span>
<button id={this.props.index} onClick={this.props.onIncrement}>Increment</button>
</div>
);
}
}
state = {
items: [{ id: 1, value: 9 }, { id: 2, value: 10 }, { id: 3, value: 0 }]
};
handleIncrement = id => event => {
event.preventDefault();
const s = JSON.parse(JSON.stringify(this.state.items)); // dereference
const ndx = s.map(e => e.id).indexOf(id);
s[ndx]["value"]++;
this.setState({ items: s });
};
here's a sandbox you can preview with the implementation:
https://codesandbox.io/s/wonderful-voice-kkq7b?file=/src/Increment.js:380-803
I am new to react and I've been trying to achieve a function that I am not sure of, I have a component that renders JSON file and shows products named 'product list', another component named 'person' which is used to show product items, both are working fine, but the third component called menucat includes the scrolling menu from https://www.npmjs.com/package/react-horizontal-scrolling-menu, the onselect function of the menu component returns an id number on selection, I want to pass that number inside the mapping function within the productlist.
Product list
import React from "react";
import Person from "./Person";MenuCat";
import MenuCat, {a, onSelect, selected} from "../components/
class ProductList extends React.Component {
state = {
error: null,
isLoaded: false,
items: []
};
componentDidMount() {
fetch("items.json")
.then(res => res.json())
.then(
result => {
this.setState({
isLoaded: true,
items: result
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
);
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return (
<div>
Error:{" "}
{error.message }
{console.log("check 1:", items)}
</div>
);
} else if (!isLoaded) {
return (
<div>
<img
src="loading.gif"
alt="loading"
height="100"
/>
</div>
);
} else {
return (
<div>
<MenuCat />
<div className="row">
{items.children[0].children.map(item => (
<Person
className="person"
Key={item.name}
Title={item.name}
imgSrc={item.image_url}
>
{item.base_price}
</Person>
))}
</div>
</div>
);
}
}
}
and the menuCat component looks like this
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "../menu.css";
// list of items
const list = [
{ name: "category1" , id : 0},
{ name: "category2" , id : 1},
{ name: "category3" , id : 2},
{ name: "category4" , id : 3},
{ name: "category5" , id : 4},
{ name: "category6" , id : 5},
{ name: "category7" , id : 6},
];
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return <div className="menu-item">{text}</div>;
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name } = el;
const { id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
const ArrowLeft = Arrow({ text: "<", className: "arrow-prev" });
const ArrowRight = Arrow({ text: ">", className: "arrow-next" });
export class Menucat extends Component {
state = {
selected: "0"
};
onSelect = key => {
console.log(`onSelect: ${key}`);
this.setState({ selected: key});
};
render() {
const { selected } = this.state;
// Create menu from items
const menu = Menu(list, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
arrowLeft={ArrowLeft}
arrowRight={ArrowRight}
selected={selected}
onSelect={this.onSelect}
/>
</div>
);
}
}
export default Menucat;
I want the id generated from onselect function to be added instead of 0 in
{items.children[0].children.map(item => (
so that whenever the user clicks on a category item, the id of that category goes to the mapping function which will do the rest. I am aware that the category list is hardcoded, for now, I just want this communication between the components to happen, I want to pass the id from the menucat component to a something like a variable in product list that can go instead of the zero like {items.children[selected].children.map(item => (
you'll want to add an 'onSelect' prop to MenuCat to pass through the ScrollMenu's onSelect results, then a state value in ProductList to store the selected key. something like this:
ProductList
import React from "react";
import Person from "./Person";MenuCat";
import MenuCat, {a, onSelect, selected} from "../components/
class ProductList extends React.Component {
state = {
error: null,
isLoaded: false,
items: [],
selected: 0,
};
componentDidMount() {
fetch("items.json")
.then(res => res.json())
.then(
result => {
this.setState({
isLoaded: true,
items: result
});
},
error => {
this.setState({
isLoaded: true,
error
});
}
);
}
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect(key) {
this.setState({selected: key});
}
render() {
const { error, isLoaded, items } = this.state;
if (error) {
return (
<div>
Error:{" "}
{error.message }
{console.log("check 1:", items)}
</div>
);
} else if (!isLoaded) {
return (
<div>
<img
src="loading.gif"
alt="loading"
height="100"
/>
</div>
);
} else {
return (
<div>
<MenuCat onSelect={this.onSelect} />
<div className="row">
{items.children[this.state.selected].children.map(item => (
<Person
className="person"
Key={item.name}
Title={item.name}
imgSrc={item.image_url}
>
{item.base_price}
</Person>
))}
</div>
</div>
);
}
}
}
and MenuCat like
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "../menu.css";
// list of items
const list = [
{ name: "category1" , id : 0},
{ name: "category2" , id : 1},
{ name: "category3" , id : 2},
{ name: "category4" , id : 3},
{ name: "category5" , id : 4},
{ name: "category6" , id : 5},
{ name: "category7" , id : 6},
];
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return <div className="menu-item">{text}</div>;
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name } = el;
const { id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
const ArrowLeft = Arrow({ text: "<", className: "arrow-prev" });
const ArrowRight = Arrow({ text: ">", className: "arrow-next" });
export class Menucat extends Component {
state = {
selected: "0"
};
onSelect = key => {
console.log(`onSelect: ${key}`);
this.setState({ selected: key});
this.props.onSelect(key);
};
render() {
const { selected } = this.state;
// Create menu from items
const menu = Menu(list, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
arrowLeft={ArrowLeft}
arrowRight={ArrowRight}
selected={selected}
onSelect={this.onSelect}
/>
</div>
);
}
}
export default Menucat;
you pass an 'onSelect' function to menucat, menucat calls it when the item is selected, and back in ProductList, its 'onSelect' function is then run, setting state which can then be used in your item selection.
make sense?
I have a button that toggles a drop down menu. Within this menu a user can toggle check-boxes, apply the changes or cancel.
Cancel should reset the check-boxes to the previous applied changes.
I am struggling to reset the state of the FilterMenu component. I attempted to stringify the initial state, however this has proven to be limited.
How can reset the state of the FilterMenu component to the previous applied changes?
When the user cancels:
Filter Component
//close menu and reset state
handleCancel() {
this.setState({menuOpen: false});
this.props.resetState();
}
resetState is called which parses my initial state and sets it.
Main Component
resetState() {
this.setState(JSON.parse(this.baseState));
}
Snippet
const FilterMenu = ({checkBoxes, handleChange, handleCancel, handleSave}) => {
return (
<div>
{Object.keys(checkBoxes).map(key => {
return (
<div key={key}>
<label htmlFor={key}>{checkBoxes[key].name}</label>
<input
id={key}
type="checkbox"
checked={checkBoxes[key].isChecked}
onChange={handleChange}
/>
</div>
);
})}
<button onClick={handleCancel}>Cancel</button>
<button onClick={handleSave}>Save</button>
</div>
);
};
class Filter extends React.Component {
constructor(props) {
super(props);
this.state = {
menoOpen: false,
};
this.handleCancel = this.handleCancel.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleCancel() {
this.setState({menuOpen: false});
this.props.resetState();
}
handleButtonClick() {
this.setState({menuOpen: !this.state.menuOpen});
}
render() {
return (
<div>
<button onClick={this.handleButtonClick}>Choose Fruits</button>
{this.state.menuOpen && (
<FilterMenu
checkBoxes={this.props.checkBoxes}
handleSave={this.props.handleSave}
handleCancel={this.handleCancel}
handleChange={this.props.handleChange}
/>
)}
</div>
);
}
}
//Parent component
class Main extends React.Component {
constructor() {
super();
this.state = {
checkBoxes: {
1: {
name: 'Apple',
isChecked: true,
},
2: {
name: 'Pear',
isChecked: true,
},
3: {
name: 'Tomato',
isChecked: true,
},
},
fruit: {
1: {
name: 'Apple',
},
3: {
name: 'Apple',
},
4: {
name: 'Pear',
},
5: {
name: 'Tomato',
},
6: {
name: 'Apple',
},
},
checkedBoxes: [],
};
this.baseState = JSON.stringify(this.state);
this.fruitFilter = this.fruitFilter.bind(this);
this.handleSave = this.handleSave.bind(this);
this.handleChange = this.handleChange.bind(this);
this.resetState = this.resetState.bind(this);
}
resetState() {
this.setState(JSON.parse(this.baseState));
}
//populates the checkedboxs array with name to filter by
handleSave() {
//look at the checked boxes
//filter the fruit based on the ones that are checked`
const checkedBoxes = Object.keys(this.state.checkBoxes)
.filter(key => {
//return name of fruit if it is checked
return this.state.checkBoxes[key].isChecked
? this.state.checkBoxes[key].name
: false;
})
.reduce((a, b) => {
a.push(this.state.checkBoxes[b].name);
return a;
}, []);
console.log(checkedBoxes);
this.setState({checkedBoxes: checkedBoxes});
}
//handles the checkbox toggle
handleChange(e) {
const checkBoxes = {...this.state.checkBoxes};
checkBoxes[e.target.id].isChecked = e.target.checked;
this.setState({checkBoxes: checkBoxes});
}
//filteres the fruit - if nothing is checked return them all
fruitFilter(fruit) {
return Object.keys(fruit)
.filter(key => {
return (
this.state.checkedBoxes.length <= 0 ||
this.state.checkedBoxes.indexOf(fruit[key].name) > -1
);
})
.reduce((a, b) => {
a[b] = fruit[b];
return a;
}, {});
}
render() {
const visibleFruits = this.fruitFilter(this.state.fruit);
return (
<div>
<Filter
resetState={this.resetState}
checkBoxes={this.state.checkBoxes}
handleSave={this.handleSave}
handleChange={this.handleChange}
/>
<div>
<h2>Filtered Fruit</h2>
{Object.keys(visibleFruits).map(key => {
return (
<div key={key}>
<span>{visibleFruits[key].name}</span>
</div>
);
})}
</div>
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('react'))
<div id="react"></div>
<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>
The problem is you are not saving the previous state before modifying it. Remember the constructor is only called once.
The code i have added in handleSave
this.baseState = JSON.stringify(this.state.checkBoxes)
and in resetState
this.setState({checkBoxes: JSON.parse(this.baseState)});
Here is the modified version:
const FilterMenu = ({checkBoxes, handleChange, handleCancel, handleSave}) => {
return (
<div>
{Object.keys(checkBoxes).map(key => {
return (
<div key={key}>
<label htmlFor={key}>{checkBoxes[key].name}</label>
<input
id={key}
type="checkbox"
checked={checkBoxes[key].isChecked}
onChange={handleChange}
/>
</div>
);
})}
<button onClick={handleCancel}>Cancel</button>
<button onClick={handleSave}>Save</button>
</div>
);
};
class Filter extends React.Component {
constructor(props) {
super(props);
this.state = {
menoOpen: false,
};
this.handleCancel = this.handleCancel.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
}
handleCancel() {
// this.setState({menuOpen: false});
this.props.resetState();
}
handleButtonClick() {
this.setState({menuOpen: !this.state.menuOpen});
}
render() {
return (
<div>
<button onClick={this.handleButtonClick}>Choose Fruits</button>
{this.state.menuOpen && (
<FilterMenu
checkBoxes={this.props.checkBoxes}
handleSave={this.props.handleSave}
handleCancel={this.handleCancel}
handleChange={this.props.handleChange}
/>
)}
</div>
);
}
}
//Parent component
class Main extends React.Component {
constructor() {
super();
this.state = {
checkBoxes: {
1: {
name: 'Apple',
isChecked: true,
},
2: {
name: 'Pear',
isChecked: true,
},
3: {
name: 'Tomato',
isChecked: true,
},
},
fruit: {
1: {
name: 'Apple',
},
3: {
name: 'Apple',
},
4: {
name: 'Pear',
},
5: {
name: 'Tomato',
},
6: {
name: 'Apple',
},
},
checkedBoxes: ['Apple', 'Pear', 'Tomato'],
};
this.baseState = JSON.stringify(this.state.checkBoxes)
this.fruitFilter = this.fruitFilter.bind(this);
this.handleSave = this.handleSave.bind(this);
this.handleChange = this.handleChange.bind(this);
this.resetState = this.resetState.bind(this);
}
resetState() {
console.log(this.baseState)
this.setState({checkBoxes: JSON.parse(this.baseState)});
}
//populates the checkedboxs array with name to filter by
handleSave() {
//look at the checked boxes
//filter the fruit based on the ones that are checked`
this.baseState = JSON.stringify(this.state.checkBoxes)
const checkedBoxes = Object.keys(this.state.checkBoxes)
.filter(key => {
//return name of fruit if it is checked
return this.state.checkBoxes[key].isChecked
? this.state.checkBoxes[key].name
: false;
})
.reduce((a, b) => {
a.push(this.state.checkBoxes[b].name);
return a;
}, []);
console.log(checkedBoxes);
this.setState({checkedBoxes: checkedBoxes});
}
//handles the checkbox toggle
handleChange(e) {
const checkBoxes = {...this.state.checkBoxes};
checkBoxes[e.target.id].isChecked = e.target.checked;
this.setState({checkBoxes: checkBoxes});
}
//filteres the fruit - if nothing is checked return them all
fruitFilter(fruit) {
return Object.keys(fruit)
.filter(key => {
return (
this.state.checkedBoxes.length <= 0 ||
this.state.checkedBoxes.indexOf(fruit[key].name) > -1
);
})
.reduce((a, b) => {
a[b] = fruit[b];
return a;
}, {});
}
render() {
const visibleFruits = this.fruitFilter(this.state.fruit);
return (
<div>
<Filter
resetState={this.resetState}
checkBoxes={this.state.checkBoxes}
handleSave={this.handleSave}
handleChange={this.handleChange}
/>
<div>
<h2>Filtered Fruit</h2>
{Object.keys(visibleFruits).map(key => {
return (
<div key={key}>
<span>{visibleFruits[key].name}</span>
</div>
);
})}
</div>
</div>
);
}
}
ReactDOM.render(<Main />, document.getElementById('react'))
<div id="react"></div>
<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>
Please note that using JSON.stringify to clone objects is discouraged due to performance issues and for a real world application you should use proper deepCloning method (either from a library or your own implementation)
I have a bootstrap grid where each grid item is populated from an array of objects but after each grid item I would like to have a vote button. How could I achieve this with maintaining state on each button separately, ie when button 1 is clicked the text should change from 'vote' to 'voted' whilst the others remain as 'vote'.
At the moment when a button is clicked, all of them change to 'Voted'
class Items extends Component {
constructor(props) {
super(props);
this.state = { hasVoted: false };
this.OnClick = this.OnClick.bind(this);
}
OnClick() {
this.setState(prevState => ({
hasVoted: !prevState.hasVoted
}));
}
render() {
const Item = teasers.items.map(item =>
<Col key={item.nid}>
<span>
{itemType}
</span>
<a href={item.path}>
<Image src={item.image.src} title={item.productType} />
<span>
{item.Title}
</span>
<div className={teasersStyle.copy}>
{" "}{item.Copy}>
</div>
</a>
<div
className={this.state.hasVoted ? "active" : "notactive"}
onClick={this.OnClick}
>
{this.state.hasVoted ? "Voted" : "Vote"}
</div>
</Col>
);
return (
<div>
<Grid>
<Row>
{Item}
</Row>
</Grid>
</div>
);
}
}
export default Items;
I have created a simple example for you:
class App extends React.Component {
constructor() {
super();
this.onClick = this.onClick.bind(this);
this.state = {
arr: [
{ name: "first", isActive: true },
{ name: "second", isActive: true },
{ name: "third", isActive: true },
{ name: "fourth", isActive: true }
]
};
}
onClick(index) {
let tmp = this.state.arr;
tmp[index].isActive = !tmp[index].isActive;
this.setState({ arr: tmp });
}
render() {
return (
<div>
{this.state.arr.map((el, index) =>
<div key={index} onClick={() => this.onClick(index)}>
name: {el.name} / isActive: {el.isActive ? "true" : "false"}
</div>
)}
</div>
);
}
}
Check the fiddle and implement it in your case.
One more way to handle this is keeping the index of an active button in the state:
class App extends React.Component {
state = {
users: [
{ name: "John" },
{ name: "Sarah" },
{ name: "Siri" },
{ name: "Jim" },
{ name: "Simon" },
],
activeIndex: 0,
}
render() {
const { users, activeIndex } = this.state;
return (
<div>
{users.map((u, i) => (
<div
className={i === activeIndex ? 'active' : ''}
onClick={() => this.setState({ activeIndex: i })}
key={u.name}
>
{u.name}
</div>
))}
</div>
)
}
}
https://jsfiddle.net/846tfe3u/