How to handle onClick to many objects from the same array - javascript

I have some problem and am getting furios a little. I want to .map my Array of Objects. And make everyone of them clickable. After click I want the particular object to show only its Avatar:
const stations = [
{
name: 'first',
avatar: './img/1.jpg'
},
{
name: 'second',
avatar: './img/2.jpg'
},
{
name: 'third',
avatar: './img/3.jpg'
},
{
name: 'fourth',
avatar: './img/4.jpg'
},
{
name: 'fifth',
avatar: './img/5.jpg'
}
]
Right now. I can access the value I need from my Database Array. But! I have a problem with:
this.state = {
clicked: false
}
this.handleClick = this.handleClick.bind(this)
}
My objects do not have separate state. So when I want to create some action based on this.state (like hide and show) it always work on EVERY element.
I have code which works in some way. When I render the list and click on any button, action occurs for every of them:
class radiosList extends React.Component {
constructor() {
super();
this.state = {
clicked: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(station) {
console.log(this)
this.setState({ clicked: !this.state.clicked })
}
render () {
return (
<div>
{
this.props.stations.map((station, index) => (
<div className='radio_list radio_list--component' style={{cursor: 'pointer'}} onClick={() => this.handleClick(station.avatar)}>
<div className='radio_list radio_list--component radio_list--component-station_name'>{station.name}</div>
</div>
))
}
</div>
)
}
}
export default radiosList
Edit: first answer helped with accessing values I need.

This is the way you can achieve what you want by adding an additional clicked state attribute to data. There could be a better way but his is how I have done it for my purposes so far.
class radiosList extends React.Component {
constructor() {
super();
this.state = {
data: [],
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(index) {
console.log(this)
var data = [...this.state.data];
data[index].clicked = !data[index].clicked;
this.setState({data});
}
render () {
var self = this;
this.props.station.forEach(function(station) {
self.state.data.push({name: station.name, avatar: station.avatar, clicked: false});
self.setState({data: self.state.data});
})
return (
<div>
{
this.state.data.map((station, index) => (
<div className='radio_list radio_list--component' style={{cursor: 'pointer'}} onClick={() => this.handleClick(index)}>
<div className='radio_list radio_list--component radio_list--component-station_name'>{station.name}</div>
</div>
))
}
</div>
)
}
}
export default radiosList

Related

How to get value from this.state. Property of undefined

I'm trying to pass value from one component to another. First one looks like this:
class ListStation extends Component {
constructor(props) {
super(props)
this.state = {
stations: []
}
this.editStation = this.editStation.bind(this);
}
editStation(id) {
this.props.history.push(`/add-station/${id}`);
}
componentDidMount() {
StationService.getStations().then((res) => {
this.setState({ stations: res.data });
})
}
}
render() {
return (
<div>
<tbody>
{this.state.stations.map(
station =>
<tr key={station.id}>
<td>{station.city}</td>
<td>{station.name}</td>
<td>
<button onClick={() => this.editStation(station.id)} className="btn btn-info">Modify</button>
...
</div>
</div>
);
}
}
export default ListStation;
And another looks like this:
import React, { Component } from 'react';
import StationService from '../services/StationService';
class CreateStationComponent extends Component {
constructor(props) {
super(props)
this.state = {
station: {
id: this.props.match.params.id,
city: '',
name: '',
trains: [
{
number: '',
numberOfCarriages: ''
}
]
}
}
this.changeCityHandles = this.changeCityHandles.bind(this);
this.changeNameHandles = this.changeNameHandles.bind(this);
this.saveStation = this.saveStation.bind(this);
}
componentDidMount() {
if (this.state.station[0].id === '_add') {
return;
} else {
StationService.getStationById(this.state.id).then((res) => {
let station = res.data;
this.setState({ name: station[0].name, city: station[0].city })
});
}
console.log(this.state.station.city + 'dfddddd');
}
But when I try to pass value from one component to another I get error: Property of undefined. The response I get from API looks like this:
I'm trying to edit values based on the id taken from the first component but it seems to fail.
if (this.state.station[0].id === '_add') {
return;
}
Have a look at this if statement from your codebase I think you should remove [0] after this.state.station ... this is because station is an object not an Array
Change it to if (this.state.station.id === '_add') {

Update state array by object Id

I want to update state array object by particular id.
Suppose I have following object in state. And I tried to update by following way using id but, it doesn't work for me.
It didn't update state data.
this.state = {
data: [{id:'124',name:'qqq'},
{id:'589',name:'www'},
{id:'45',name:'eee'},
{id:'567',name:'rrr'}]
}
publishCurrentProject = user => {
this.setState(prevState => ({
data: prevState.data.map(item =>
item.id === user.id ? { ...user } : item
),
}))
}
let user = {id:'124',name:'ttt'};
publishCurrentProject(user);
Any help would be greatly appreciated.
Maybe the problem is on how you called the publishCurrentProject(), maybe you put that function in the wrong context. I use your implementation and it still works
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ id: "124", name: "qqq" },
{ id: "589", name: "www" },
{ id: "45", name: "eee" },
{ id: "567", name: "rrr" }
]
};
this.handleClick = this.handleClick.bind(this);
this.publishCurrentProject = this.publishCurrentProject.bind(this);
}
handleClick(e) {
let user = { id: "124", name: "ttt" };
this.publishCurrentProject(user);
}
publishCurrentProject(user) {
this.setState((prevState) => ({
data: prevState.data.map((item) =>
item.id === user.id ? { ...user } : item
)
}));
}
render() {
return (
<div className="App">
<h1>Test</h1>
{this.state.data.map((el) => (
<p>{el.name}</p>
))}
<button onClick={this.handleClick}>Change</button>
</div>
);
}
}
Codesandbox for worked example

How do I set state to toggle a boolean value that sits inside an array of JSON object?

I've been stuck on this today, and I've tried several possible solutions that didn't resolve the issue.
This is a flip-card effect I'm creating for a team's profile page. When user click on the profile-card it flips the card to show more info on the back. The way I've set up currently flips all the cards all at once when onClick is triggered, instead of the individual card. I know by doing that I need to set the current state of isTrue to render the boolean value from the JSON object, and I tried several possible ways I could think of with no succession.
I'd really appreciate if anyone can help me learn/understand how to solve this issue!
Here is the simplified example code:
JSON object:
data: [
{ id: 1, isTrue: false },
{ id: 2, isTrue: false },
{ id: 3, isTrue: false},
...
]
React Component:
import exampleData from 'data.js'
class App extends Component {
constructor () {
super()
this.state = {
isTrue: //I need to set the current state of isTrue to render from exampleData, but couldn't figure out how to do it.//
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle (e) {
e.preventDefault();
this.setState(prevState => ({
isTrue: !prevState.isTrue
}))
}
render() {
return(
<div className="App">
{exampleData.data.map((obj) => {
return <ReactCardFlip isTrue={this.state.isTrue}/>
<button onClick={handleToggle}>Flip the card</div>
</ReactCardFlip>
})
</div>
);
}
}
export default App;
You need to store externalData in your component's state. Render this list using map and pass current clicked card to the toggle method. After this, you are able to change your state depending on the element you have clicked. See example below:
import exampleData from 'data.js'
class App extends Component {
constructor () {
super()
this.state = {
cards: [],
}
this.handleToggle = this.handleToggle.bind(this);
}
componentDidMount() {
this.state.setState({ cards: exampleData });
}
handleToggle (card) {
this.setState(prevState => ({
cards: prevState.cards.map(prevCard => {
if (prevCard.id === card.id) {
return {
id: prevCard.id,
isTrue: !prevCard.isTrue
}
}
return prevCard;
})}));
}
render() {
return (
<div className="App">
{this.state.data.map((card) => {
return (
<ReactCardFlip isTrue={card.isTrue}>
<button onClick={() => this.handleToggle(card)}>Flip the card</button>
</ReactCardFlip>
)
})}
</div>
);
}
}
export default App;
The main is each item should have its own isTure
So the single state isTrue cannot achieve your demand.
Two ways:
exampleData in state
isTure => arrIsTrue to correspond each item in exampleData
data: [
{ id: 1, isTrue: false },
{ id: 2, isTrue: false },
{ id: 3, isTrue: false},
...
]
these data can place in state
like after: this.state = { exampleData: exampleData.data }
so the render can be <ReactCardFlip isTrue={obj.isTrue}/>
also the handleToggle should get current toggle index to after:
<div className="App">
{this.state.exampleData.map((obj, index) => {
return <ReactCardFlip key={index} isTrue={this.state.isTrue}/>
<button onClick={() => handleToggle(index)}>Flip the card</div>
</ReactCardFlip>
})
</div>
and then in handleToggle to be:
handleToggle (index) {
const exampleData = this.state.exampleData
exampleData[index] = !exampleData[index].isTrue
this.setState({ exampleData: exampleData })
}
After all, don't forget key

Selecting Multiple options in Reactjs

I have some data in arrays. I am getting it by using map, as you see in the below example. Also, i pass that into the button. Now, if, i select a button, it will get selected. But, if i select the next button, the previous button will get unselected and the current button will get selected. I don't want it to happen. I want to select multi buttons, if it all get clicked.
Thanks in advance.
Below is the Solution
import React, { Component } from 'react';
const BUTTONS = [
{id:0, title:'button1'},
{id:1, title:'button2'},
{id:2, title:'button3'}
]
class Map extends Component {
constructor(props){
super(props);
this.state = {
values: []
}
}
handleButton = button => {
let tmp = this.state.values;
if (this.state.values.includes(button)) {
this.setState({
values: this.state.values.filter(el => el !== button)
})
} else {
tmp.push(button);
this.setState({
values: tmp
})
}
}
render () {
return (
<div>
{BUTTONS.map(bt=>(
<button
key={bt.id}
onClick={()=>this.handleButton(bt.id)}
className={this.state.values.includes(bt.id) ? "buttonPressed" : "button"}>
{bt.title}
</button>
))}
</div>
);
}
}
export default Map;
Selecting multiple buttons
you'd better use the state as an array.
this.state = {
values: []
}
and you can push items.
let tmp = this.state.values;
tmp.push(button);
this.setState({
values: tmp
});
in render() you have to check state.values has bt.id
className={this.state.values.includes(bt.id) ? "buttonPressed" : "button"
Toggling multiple buttons
you can check in handleButton() whether that selected button is already selected
handleButton = button => {
if (this.state.values.includes(button)) {
this.setState({
values: this.state.values.filter(el => el !== button)
})
}
const BUTTONS = [
{ id: 0, title: 'button1' },
{ id: 1, title: 'button2' },
{ id: 2, title: 'button3' }
]
class Map extends React.Component {
constructor(props) {
super(props);
this.state = {
values: []
}
}
handleButton = button => {
let tmp = this.state.values;
if (this.state.values.includes(button)) {
this.setState({
values: this.state.values.filter(el => el !== button)
})
} else {
tmp.push(button);
this.setState({
values: tmp
})
}
}
render() {
return (
<div>
{BUTTONS.map(bt => (
<button
key={bt.id}
onClick={() => this.handleButton(bt.id)}
className={this.state.values.includes(bt.id) ? "buttonPressed" : "button"}>
{bt.title}
</button>
))}
</div>
);
}
}
export default Map;
The reason your button is getting deselected is because you're overwriting this.state.value every time you click a button.
If you want multiple selections, you'll need to hold all of the selected items in the state, as an array, and then when rendering, check if the button id is included in that array.
Something like:
import React, { Component } from 'react';
const BUTTONS = [
{id:0, title:'button1'},
{id:1, title:'button2'},
{id:2, title:'button3'}
]
class Map extends Component {
constructor(props){
super(props);
this.state = {
selectedValues: []
}
}
handleButton = buttonId => {
let newSelectedValues = this.state.selectedValues;
newSelectedValues.push(buttonId);
this.setState({
selectedValues: newSelectedValues
})
}
render () {
return (
<div>
{BUTTONS.map(bt => (
<button
key={bt.id}
onClick={()=>this.handleButton(bt.id)}
className={this.state.selectedValues.includes(bt.id) ? "buttonPressed" : "button"}>
{bt.title}
</button>
))}
</div>
);
}
}
export default Map;
if you need multiple selections you need an array:
this.state = {
values:[]
}
and push it on each clicks
Correct way to push into state array

Change Specific Property of the State

I am learning ReactJS and needless to say I am an absolute beginner! I am trying to change a specific property in the array of objects which belongs to state. Every object has two properties: name and active. active values are false by default. When I click on the item, I want to change this item's active value to true.
My array is shown inside of the list element and every list element has onClick={() => props.onChangeSelected(lang.name)} method. onChangeSleceted method goes to handleChangeSelected(name) function, however, I couldn't figure out what to write inside of this function.
class Loading extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'Loading'
};
}
componentDidMount() {
const stopper = this.state.text + '...';
this.interval = window.setInterval(() => {
this.state.text === stopper
? this.setState(() => ({ text: 'Loading' }))
: this.setState((prevState) => ({ text: prevState.text + '.' }))
}, 300)
}
componentWillUnmount() {
window.clearInterval(this.interval);
}
render() {
return (
<p>
{this.state.text}
</p>
)
}
}
function LanguageList (props) {
return (
<div>
<h3>Choose your favorite:</h3>
<ul>
{props.list.map((lang) => (
<li key={lang.name} onClick={() => props.onChangeSelected(lang.name)}>
<span>{lang.name}</span>
</li>
))}
</ul>
</div>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
languages: [
{
name: 'all',
active: true
},
{
name: 'javascript',
active: false
},
{
name: 'ruby',
active: false
},
{
name: 'python',
active: false
}
]
}
this.handleChangeSelected = this.handleChangeSelected.bind(this)
}
handleChangeSelected(name) {
this.setState((currentState) => {
const lang = currentState.languages.find((lang) => lang.name === name)
return {}
})
}
render() {
return (
<div>
<LanguageList
list={this.state.languages}
onChangeSelected={this.handleChangeSelected}
/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
</script>
You can do it in a number of ways. All you need to make sure is that you aren't mutating the original state array
handleChangeSelected(name) {
this.setState((currentState) => {
return { languages: currentState.languages.map((lang) => {
if(lang.name === name) {
return {...lang, active: true};
}
return lang;
});
})
}
Try this?
handleChangeSelected(name){
// Find matching element in state
var temp = this.state.languages;
for (var i = 0; i < temp.length; i++){
if (temp[i]["name"] === name){
temp[i]["active"] = true;
}
}
this.setState({
languages: temp
});
}
As listed in the React docs, they recommend creating a new object when calling the setState function. This is of course talking about the updater function syntax (this.setState((prevState, props) => {return {...};});), which I assume the same logic is applied to the syntax used above (passing an object into set state)
The first argument [to setState] is an updater function with the signature:
(prevState, props) => stateChange
(prevState, props) => stateChange prevState is a reference to the
previous state. It should not be directly mutated. Instead, changes
should be represented by building a new object based on the input from
prevState and props.

Categories