I can't access the value of the array in state - javascript

Problem: I have a state. And I want to reach the value in this state. But the value in the array.
state = {
base: [ {tomato: false}, {egg: false} ],
contents: [
{mushroom: false},
{olive: false},
{greenPepper: false},
{sausage: false},
{tomato: false},
{redPapper: false}
]
};
class Preivew extends React.Component{
state = {
base: this.props.contents.base
};
getBase = () => {
return(
this.state.base.map( (value) => {
console.log(value)
} )
)
}
render(){
return(
<Wrap>
<div className={classes.imageBox}>
<div className={classes.image}>
{console.log(this.getBase())}
</div>
</div>
</Wrap>
)
}
};
output:
{tomato: false}
{egg: false}
I want to reach "false" here. Because I'm going to do check operations with "if else".

Use Object.values.
getBase = () => this.state.base.map(value => Object.values(value)[0])

You can get the first element of Object.values()
getBase = () => {
return(
this.state.base.map( (value) => {
console.log(Object.values(value)[0])
} )
)
}

this.state.base.map( (value) => {
Object.keys(value).forEach(key => {
console.log(value[key])
})
}
You can solve this using Object.keys in combination with forEach.

Update this property
getBase = (ingredient) => {
const ingPresent = this.state.base.find(value => value[ingredient] != undefined);
return ingPresent ? ingPresent[ingredient] : false;
};
And in the render
render(){
{this.getBase('tomato') && (
<p>Hello tomato present</p>
)}
}
The function also handles the case when the base array doesn't contain the ingredient

Related

How can I change boolean value in objects?

I have such situation:
There is an array:
const arr = [
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
]
Then I use this array in my React component to get values, but here I also need to change status to true or false in current element (not for all objects)
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => !status}>{div.title}</div> // here
</div>
)
})
So how can I make this?
How about simply (if you have the access to the arr variable) you have an option of getting an index of the array as the 2nd parameter in the map function.
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => {
arr[ix].status = !arr[ix].status
}}>{div.title}</div> // here
</div>
)
})
or pull it out in a function (better way) as
function handleClick (ix) {
const currentStatus = arr[ix].status
arr[ix].status = !currentStatus
}
arr.map((el, ix) => {
return (
<div key={el.id}>
<div onClick={() => handleClick(ix)}>{div.title}</div> // here
</div>
)
})
EDIT: Didn't see it was a reactJS, my bad, in that case the best case is to manage it with state.
You can try:
const handleClick = (el) => () => {
el.status = !el.status
}
arr.map(el => {
return (
<div key={el.id}>
<div onClick={handleClick(el)}>{div.title}</div> // here
</div>
)
})
Maintain array in react state;
const [arr,setArr]=useState([
{
id: 0,
title: 'a',
status: false
},
{
id: 1,
title: 'a',
status: false
},
{
id: 2,
title: 'a',
status: false
},
])
Write a function to handle the change in state
const changeStatus =id=>{
const newArr = arr.map((el,index)=>{
if(el.id===id){
return {...el,status:!el.status}
}
else return;
})
setArr(newArr)
}
call this function on onClick.
arr.map(el => {
return (
<div key={el.id}>
<div onClick={() => changeStatus(el.id)}>{div.title}</div> // here
</div>
)
})

how to add multiple objects in reactjs?

I want to add new Objects when user click on checkbox. For example , When user click on group , it will store data {permission:{group:["1","2"]}}. If I click on topgroup , it will store new objects with previous one
{permission:{group:["1","2"]},{topGroup:["1","2"]}}.
1st : The problem is that I can not merge new object with previous one . I saw only one objects each time when I click on the group or topgroup.
onChange = value => checked => {
this.setState({ checked }, () => {
this.setState(prevState => {
Object.assign(prevState.permission, { [value]: this.state.checked });
});
});
};
<CheckboxGroup
options={options}
value={checked}
onChange={this.onChange(this.props.label)}
/>
Here is my codesanbox:https://codesandbox.io/s/stackoverflow-a-60764570-3982562-v1-0qh67
It is a lot of code because I've added set and get to set and get state. Now you can store the path to the state in permissionsKey and topGroupKey. You can put get and set in a separate lib.js.
In this example Row is pretty much stateless and App holds it's state, this way App can do something with the values once the user is finished checking/unchecking what it needs.
const Checkbox = antd.Checkbox;
const CheckboxGroup = Checkbox.Group;
class Row extends React.Component {
isAllChecked = () => {
const { options, checked } = this.props;
return checked.length === options.length;
};
isIndeterminate = () => {
const { options, checked } = this.props;
return (
checked.length > 0 && checked.length < options.length
);
};
render() {
const {
options,
checked,
onChange,
onToggleAll,
stateKey,
label,
} = this.props; //all data and behaviour is passed by App
return (
<div>
<div className="site-checkbox-all-wrapper">
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={e =>
onToggleAll(e.target.checked, stateKey)
}
checked={this.isAllChecked()}
>
Check all {label}
</Checkbox>
<CheckboxGroup
options={options}
value={checked}
onChange={val => {
onChange(stateKey, val);
}}
/>
</div>
</div>
);
}
}
//helper from https://gist.github.com/amsterdamharu/659bb39912096e74ba1c8c676948d5d9
const REMOVE = () => REMOVE;
const get = (object, path, defaultValue) => {
const recur = (current, path) => {
if (current === undefined) {
return defaultValue;
}
if (path.length === 0) {
return current;
}
return recur(current[path[0]], path.slice(1));
};
return recur(object, path);
};
const set = (object, path, callback) => {
const setKey = (current, key, value) => {
if (Array.isArray(current)) {
return value === REMOVE
? current.filter((_, i) => key !== i)
: current.map((c, i) => (i === key ? value : c));
}
return value === REMOVE
? Object.entries(current).reduce((result, [k, v]) => {
if (k !== key) {
result[k] = v;
}
return result;
}, {})
: { ...current, [key]: value };
};
const recur = (current, path) => {
if (path.length === 1) {
return setKey(
current,
path[0],
callback(current[path[0]])
);
}
return setKey(
current,
path[0],
recur(current[path[0]], path.slice(1))
);
};
return recur(object, path, callback);
};
class App extends React.Component {
state = {
permission: { group: [] },
topGroup: [],
some: { other: [{ nested: { state: [] } }] },
};
permissionsKey = ['permission', 'group']; //where to find permissions in state
topGroupKey = ['topGroup']; //where to find top group in state
someKey = ['some', 'other', 0, 'nested', 'state']; //where other group is in state
onChange = (key, value) => {
//use set helper to set state
this.setState(set(this.state, key, arr => value));
};
isIndeterminate = () =>
!this.isEverythingChecked() &&
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result || get(this.state, key).length,
false
);
toggleEveryting = e => {
const checked = e.target.checked;
this.setState(
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
set(result, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
),
this.state
)
);
};
onToggleAll = (checked, key) => {
this.setState(
//use set helper to set state
set(this.state, key, () =>
checked
? this.plainOptions.map(({ value }) => value)
: []
)
);
};
isEverythingChecked = () =>
[
this.permissionsKey,
this.topGroupKey,
this.someKey,
].reduce(
(result, key) =>
result &&
get(this.state, key).length ===
this.plainOptions.length,
true
);
plainOptions = [
{ value: 1, name: 'Apple' },
{ value: 2, name: 'Pear' },
{ value: 3, name: 'Orange' },
];
render() {
return (
<React.Fragment>
<h1>App state</h1>
{JSON.stringify(this.state)}
<div>
<Checkbox
indeterminate={this.isIndeterminate()}
onChange={this.toggleEveryting}
checked={this.isEverythingChecked()}
>
Toggle everything
</Checkbox>
</div>
{[
{ label: 'group', stateKey: this.permissionsKey },
{ label: 'top', stateKey: this.topGroupKey },
{ label: 'other', stateKey: this.someKey },
].map(({ label, stateKey }) => (
<Row
key={label}
options={this.plainOptions}
// use getter to get state selected value
// for this particular group
checked={get(this.state, stateKey)}
label={label}
onChange={this.onChange} //change behaviour from App
onToggleAll={this.onToggleAll} //toggle all from App
//state key to indicate what state needs to change
// used in setState in App and passed to set helper
stateKey={stateKey}
/>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<link href="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.css" rel="stylesheet"/>
<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>
<script src="https://cdnjs.cloudflare.com/ajax/libs/antd/4.0.3/antd.js"></script>
<div id="root"></div>
I rewrite all the handlers.
The bug in your code is located on the usage of antd Checkbox.Group component with map as a child component, perhaps we need some key to distinguish each of the Row. Simply put them in one component works without that strange state update.
As the demand during communication, the total button is also added.
And, we don't need many states, keep the single-source data is always the best practice.
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Checkbox } from "antd";
const group = ["group", "top"];
const groupItems = ["Apple", "Pear", "Orange"];
const CheckboxGroup = Checkbox.Group;
class App extends React.Component {
constructor() {
super();
this.state = {
permission: {}
};
}
UNSAFE_componentWillMount() {
this.setDefault(false);
}
setDefault = fill => {
const temp = {};
group.forEach(x => (temp[x] = fill ? groupItems : []));
this.setState({ permission: temp });
};
checkLength = () => {
const { permission } = this.state;
let sum = 0;
Object.keys(permission).forEach(x => (sum += permission[x].length));
return sum;
};
/**
* For total
*/
isTotalIndeterminate = () => {
const len = this.checkLength();
return len > 0 && len < groupItems.length * group.length;
};
onCheckTotalChange = () => e => {
this.setDefault(e.target.checked);
};
isTotalChecked = () => {
return this.checkLength() === groupItems.length * group.length;
};
/**
* For each group
*/
isIndeterminate = label => {
const { permission } = this.state;
return (
permission[label].length > 0 &&
permission[label].length < groupItems.length
);
};
onCheckAllChange = label => e => {
const { permission } = this.state;
const list = e.target.checked ? groupItems : [];
this.setState({ permission: { ...permission, [label]: list } });
};
isAllChecked = label => {
const { permission } = this.state;
return !groupItems.some(x => !permission[label].includes(x));
};
/**
* For each item
*/
isChecked = label => {
const { permission } = this.state;
return permission[label];
};
onChange = label => e => {
const { permission } = this.state;
this.setState({ permission: { ...permission, [label]: e } });
};
render() {
const { permission } = this.state;
console.log(permission);
return (
<React.Fragment>
<Checkbox
indeterminate={this.isTotalIndeterminate()}
onChange={this.onCheckTotalChange()}
checked={this.isTotalChecked()}
>
Check all
</Checkbox>
{group.map(label => (
<div key={label}>
<div className="site-checkbox-all-wrapper">
<Checkbox
indeterminate={this.isIndeterminate(label)}
onChange={this.onCheckAllChange(label)}
checked={this.isAllChecked(label)}
>
Check all
</Checkbox>
<CheckboxGroup
options={groupItems}
value={this.isChecked(label)}
onChange={this.onChange(label)}
/>
</div>
</div>
))}
</React.Fragment>
);
}
}
ReactDOM.render(<App />, document.getElementById("container"));
Try it online:
Please try this,
onChange = value => checked => {
this.setState({ checked }, () => {
this.setState(prevState => {
permission : { ...prevSatate.permission , { [value]: this.state.checked }}
});
});
};
by using spread operator you can stop mutating the object. same way you can also use object.assign like this.
this.setState(prevState => {
permission : Object.assign({} , prevState.permission, { [value]: this.state.checked });
});
And also i would suggest not to call setState in a callback. If you want to access the current state you can simply use the current checked value which you are getting in the function itself.
so your function becomes ,
onChange = value => checked => {
this.setState({ checked });
this.setState(prevState => {return { permission : { ...prevSatate.permission, { [value]: checked }}
}});
};
Try the following
//Inside constructor do the following
this.state = {checkState:[]}
this.setChecked = this.setChecked.bind(this);
//this.setChecked2 = this.setChecked2.bind(this);
//Outside constructor but before render()
setChecked(e){
this.setState({
checkState : this.state.checkState.concat([{checked: e.target.id + '=>' + e.target.value}])
//Id is the id property for a specific(target) field
});
}
//Finally attack the method above.i.e. this.setChecked to a form input.
Hope it will address your issues

Adding clicked items to new array in React

I am making API calls and rendering different components within an object. One of those is illustrated below:
class Bases extends Component {
constructor() {
super();
this.state = {
'basesObject': {}
}
}
componentDidMount() {
this.getBases();
}
getBases() {
fetch('http://localhost:4000/cupcakes/bases')
.then(results => results.json())
.then(results => this.setState({'basesObject': results}))
}
render() {
let {basesObject} = this.state;
let {bases} = basesObject;
console.log(bases);
//FALSY values: undefined, null, NaN, 0, false, ""
return (
<div>
{bases && bases.map(item =>
<button key={item.key} className="boxes">
{/* <p>{item.key}</p> */}
<p>{item.name}</p>
<p>${item.price}.00</p>
{/* <p>{item.ingredients}</p> */}
</button>
)}
</div>
)
}
}
The above renders a set of buttons. All my components look basically the same.
I render my components here:
class App extends Component {
state = {
ordersArray: []
}
render() {
return (
<div>
<h1>Bases</h1>
<Bases />
<h1>Frostings</h1>
<Frostings />
<h1>Toppings</h1>
<Toppings />
</div>
);
}
}
I need to figure out the simplest way to, when a button is clicked by the user, add the key of each clicked element to a new array and I am not sure where to start. The user must select one of each, but is allowed to select as many toppings as they want.
Try this
We can use the same component for all categories. All the data is handled by the parent (stateless component).
function Buttons({ list, handleClick }) {
return (
<div>
{list.map(({ key, name, price, isSelected }) => (
<button
className={isSelected ? "active" : ""}
key={key}
onClick={() => handleClick(key)}
>
<span>{name}</span>
<span>${price}</span>
</button>
))}
</div>
);
}
Fetch data in App component, pass the data and handleClick method into Buttons.
class App extends Component {
state = {
basesArray: [],
toppingsArray: []
};
componentDidMount() {
// Get bases and toppings list, and add isSelected attribute with default value false
this.setState({
basesArray: [
{ key: "bases1", name: "bases1", price: 1, isSelected: false },
{ key: "bases2", name: "bases2", price: 2, isSelected: false },
{ key: "bases3", name: "bases3", price: 3, isSelected: false }
],
toppingsArray: [
{ key: "topping1", name: "topping1", price: 1, isSelected: false },
{ key: "topping2", name: "topping2", price: 2, isSelected: false },
{ key: "topping3", name: "topping3", price: 3, isSelected: false }
]
});
}
// for single selected category
handleSingleSelected = type => key => {
this.setState(state => ({
[type]: state[type].map(item => ({
...item,
isSelected: item.key === key
}))
}));
};
// for multiple selected category
handleMultiSelected = type => key => {
this.setState(state => ({
[type]: state[type].map(item => {
if (item.key === key) {
return {
...item,
isSelected: !item.isSelected
};
}
return item;
})
}));
};
// get final selected item
handleSubmit = () => {
const { basesArray, toppingsArray } = this.state;
const selectedBases = basesArray.filter(({ isSelected }) => isSelected);
const selectedToppings = toppingsArray.filter(({ isSelected }) => isSelected);
// submit the result here
}
render() {
const { basesArray, toppingsArray } = this.state;
return (
<div>
<h1>Bases</h1>
<Buttons
list={basesArray}
handleClick={this.handleSingleSelected("basesArray")}
/>
<h1>Toppings</h1>
<Buttons
list={toppingsArray}
handleClick={this.handleMultiSelected("toppingsArray")}
/>
</div>
);
}
}
export default App;
CSS
button {
margin: 5px;
}
button.active {
background: lightblue;
}
I think the following example would be a good start for your case.
Define a handleClick function where you can set state with setState as the following:
handleClick(item) {
this.setState(prevState => {
return {
...prevState,
clickedItems: [...prevState.clickedItems, item.key]
};
});
}
Create an array called clickedItems in constructor for state and bind handleClick:
constructor() {
super();
this.state = {
basesObject: {},
clickedItems: [],
}
this.handleClick = this.handleClick.bind(this);
}
You need to add a onClick={() => handleClick(item)} handler for onClick:
<button key={item.key} className="boxes" onClick={() => handleClick(item)}>
{/* <p>{item.key}</p> */}
<p>{item.name}</p>
<p>${item.price}.00</p>
{/* <p>{item.ingredients}</p> */}
</button>
I hope that helps!

Update nested array values using map and index es6

I have a react form which has dynamic rows, but I'm stuck at line 76 (Demo: https://codesandbox.io/s/pw58j0vzoq), not sure what can I do to update the value in the row
class App extends React.Component {
state = {
acceptedValues: [
{
id: 1,
_arguments: ["Samsung", "xiaomi"]
},
{
id: 2,
_arguments: ["OR", "AND"]
}
]
};
handleChange = (name, index, argumentIndex) => e => {
const { acceptedValues } = this.state;
if (name === "_arguments") {
updatedState = acceptedValues.map((o, i) => {
if (i === index) {
return {
...o,
_arguments: o._arguments.map((o2, index2) => {
if (index2 === argumentIndex) {
//what to do here?
}
return o;
})
};
}
return o;
});
this.setState({
acceptedValues: updatedState
});
}
};
render() {
const { acceptedValues } = this.state;
return (
<div>
{acceptedValues.map(({ operator, _arguments }, index) => (
<div style={{ marginBottom: 20 }}>
<div>
{_arguments.map((val, argumentIndex) => (
<div>
<input
onChange={this.handleChange(
"_arguments",
index,
argumentIndex
)}
id="_arguments"
type="text"
value={val}
/>
<button onClick={this.removeArgument(index, argumentIndex)}>
-
</button>
</div>
))}
</div>
</div>
))}
</div>
);
}
}
I'm able to navigate until the correct array but stuck at how to update the value within the array, I've 2 indexs in my handleChange function.
There you are mapping an array into another, so you should just pick which string to return, like this:
_arguments: o._arguments.map(
(o2, index2) => {
if (index2 === argumentIndex) return e.target.value
return o2
}
)
Or in an even cleaner way:
_arguments: o._arguments.map(
(o2, index2) => index2 === argumentIndex ? e.target.value : o2
)

Print only clicked radio button value(React)

Print selected radio button value in console.
If all radiogroup is answered then print in console only the selected= true radioValue. for example: if NO radiobutton= true its value is 2. It should print value 2. Like that all true radiovalue should print in console.
Thanks
//array of cards coming from the backend
const data = [
{
cardName: 'Do you want sugar in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
},
{
cardName: 'Do you want milk in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
},
{
cardName: 'Do you want low-fat-milk in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
}
];
class CardsList extends React.Component {
constructor(props) {
super(props);
this.state = {
cards: [],
};
}
componentDidMount() {
setTimeout(() => {
// mimic an async server call
this.setState({ cards: data });
}, 1000);
}
onInputChange = ({ target }) => {
const { cards } = this.state;
const nexState = cards.map(card => {
if (card.cardName !== target.name) return card;
return {
...card,
options: card.options.map(opt => {
const checked = opt.radioName === target.value;
return {
...opt,
selected: checked
}
})
}
});
this.setState({ cards: nexState })
}
onSubmit = () => {
console.log(this.state.cards.map(({ cardName, options }) => {
const option = options.filter(({ selected }) => selected)[0]
return ` ${option.radioValue}`
}))
};
onReset = () => {
this.setState({cards:[]});
}
render() {
const { cards } = this.state;
return (
<div>
{
cards.length < 1 ? "Loading..." :
<div>
{cards.map((card, idx) => (
<ul>
{card.cardName}
{card.options.radioName}
{
card.options.map((lo, idx) => {
return <input
key={idx}
type="radio"
name={card.cardName}
value={lo.radioName}
checked={!!lo.selected}
onChange={this.onInputChange}
/>
})
}
</ul>
))
}
< button onClick={this.onSubmit}>Submit</button>
< button onClick={this.onReset}>Clear</button>
</div>
}
</div>
);
}
}
ReactDOM.render(<CardsList />, 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>
Change your log in onSubmit to this
console.log(this.state.cards.map(({ cardName, options }) => {
const option = options.filter(({ selected }) => selected)[0]
return `${cardName}: ${option.radioName}`
}))
This way you filter the options to the one, where selected is truthy, and take the first one.
To address your first question, just map over your this.state.cards array before doing the log and check, if there is exactly 1 option, where selected is true. If this is not the case, tell the user in whatever way you want.
Also you can remove your constructor and change it to that:
state = {
cards: [],
}
Because you do not access your props in your constructor
You can go with the answer of #george,
for you to check if either of the radio buttons is clicked for each card, you can run a validation check
let unselectedCards = this.state.cards.filter((card) => {
return !card.options[0].selected && !card.options[1].selected
});
use the unselectedCards variable to highlight the cards.
you can use map options again inside the cards map if you would be having more options.

Categories

Resources