Update a field of all elements in react functional state - reactjs

My state is
const [ formInputsProperty, setFormInputsProperty ] = useState(
[
{
id: 0,
name: "courseTitle",
type: "text",
placeholder: "Course Title",
errorMessage: "Course Title should be 3-16 characters and shouldn't include any special character!",
label: "Course Title",
pattern: "^[A-Za-z0-9]{3,16}$",
required: true,
isFocused: false
},
{
id: 1,
name: "shortDesc",
type: "text",
placeholder: "Course Short Description",
errorMessage: "Course Description should be 10-50 characters and shouldn't include any special character!",
label: "Course Short Description",
pattern: "^[A-Za-z0-9]{10,50}$",
required: true,
isFocused: false
}
]
)
Now i want to update isFocused to true of all elements of the given state. How can I do that? I tried map but was not successfull.

You can do it by using the map function and then setting the state afterwards.
Example:
const updateFocus = () => {
setFormInputsProperty((prev) => (
prev.map((item) => ({...item, isFocused: true}))
);
};
Explanation:
You access the current state with prev and then use the map function on it. With map you iterate over every element in the list and can do what ever you want with it. In this case we just set the isFocused property to true. The return value inside the map function always replaces the current item.
More information on MDN about map
More information on MDN about spread syntax

Related

Select an array inside an array in React

I'm trying to select an arrayfield inside an array. Following code is inserted in my useStore:
const useStore = create(
persist(set => ({
projectszustand: [
{
id: nanoid(),
name: 'Projekt Final 1',
notes: 'Hier sind ein paar Notizen',
begin: '01/01/2001',
end: '02/01/2001',
icon: 'https://www.skopos.de/wp-content/uploads/2021/04/Element-5.svg',
color: 'blue',
edit: false,
selected: true,
feature: [
{
id: nanoid(),
name: 'Feature Final 1',
begin: '01/01/2001',
end: '02/01/2001',
isChecked: false,
edit: false,
selected: false,
todo: [
{...and so on
So, I'm trying to go with forEach and set all selected fields in the feature array to false:
selectFeature: index => {
set(
produce(draft => {
draft.projectszustand[index].feature.forEach(element => {
element.selected = false;
});
draft.projectszustand[index].feature[index].selected =
!draft.projectszustand[index].feature[index].selected;
})
);
},
This no works. Error message is: TypeError: can't access property "feature", draft.projectszustand[index] is undefined
Has somebody an easy solution for this?
Thanks very much for helping.

update an object with useState hook in React

I have an object in board variable
Initial Data:
const [board, setBoard] = useState({
lanes: [],
});
{
lanes: [{
title: 'Bugs',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd03',
cards: [{
id: null,
title: 'Bug #1',
description: 'Can AI make memes',
}, ],
},
{
title: 'Tests',
id: 'd83706b0-b252-11ec-8845-ad6b1e4ecd04',
cards: [{
id: null,
title: 'Test #1',
description: 'Can AI make memes',
}, ],
},
],
};
I want to add a new element to the cards array but only to the first element in the lanes array. Other answers seem to point to having to use a callback pattern, but I am quite unfamiliar with this.
Thanks for any help.
As for any modification you want to do on a useState variable, you must use an arrow function inside of the "set" function.
You can do something like that :
setBoard((currentBoard)=> {
currentBoard.lanes[0].cards = [...currentBoard.lanes[0].cards, whateverCardYouWantToAdd ]
return {... currentBoard} //necessary to create a new object because else the hook won't be updated
})
Maybe with this.
const addValue = () => {
let t = [...board];
t[0].lanes.cards.push({
id: null,
title: "Bug #1",
description: "Can AI make memes"
});
};

Why does forEach loop only set the last value if finds to state. ReactJS

const CategoriesData = [
{
name: "Category1",
isActive: true,
children: [
{
name: "Category1Child",
isActive: false,
}
]
},
{
name: "Category2",
isActive: false,
},
{
name: "Category3",
isActive: true,
children: [
{
name: "Category3Child",
isActive: false,
}
]
}
];
const [disabledCategories, setDisabledCategories] = useState([]);
function notActiveCategories(categories) {
// Loop logs out at least 7 isActive: false categories.
categories.forEach((category) => {
if (category.isActive) notActiveCategories(category.children);
if (!category.isActive) {
setDisabledCategories([...disabledCategories, category]);
console.log(category);
}
});
};
useEffect(() => {
notActiveCategories(CategoriesData);
console.log(disabledCategories); // Only 1 category is in the array.
}, []);
I feel like the function the loop is in calling itself is causing the disabledCategories state to revert to when it was empty and that is leading to only the last step of the foreach to be set.
So how would i get this to loop through the categories array and have the disabledCategories state to contain all of the category objects that have isActive: false.
Which in the example of CategoriesData above, it would mean that the disabledCategories state would contain:
[
{
name: "Category1Child",
isActive: false,
},
{
name: "Category2",
isActive: false,
},
{
name: "Category3Child",
isActive: false,
},
];
Try changing your setDisabledCategories to use the previous state param that comes from setState:
setDisabledCategories(prevState => [...prevState, category])
When multiple setState calls are batched together you need to be careful so they don't override each other. Using this method ensures that your setState calls are "chained" so you always get the updated state.
Way 1: Affect after recursive loop
function notActiveCategoriesRecusive(categories) {
let notActive = []
categories.forEach((category) => {
if (category.isActive) notActive = [...notActive, ...(notActiveCategories(category.children))];
if (!category.isActive) {
notActive.push(category)
}
});
return notActive
};
function notActiveCategories(categories) {
setDisabledCategories(notActiveCategoriesRecusive(categories)
}
Way 2: Get the last state because it doesn't has time to refresh
function notActiveCategories(categories) {
categories.forEach((category) => {
if (category.isActive) notActiveCategories(category.children);
if (!category.isActive) {
setDisabledCategories(oldState => ([...oldState, category]))
}
});
};
I'd only call setState once with the filtered array:
const findInactive = data =>
data.filter(e => !e.isActive)
.concat(...data.filter(e => e.children)
.map(e => findInactive(e.children)))
;
const categoriesData = [ { name: "Category1", isActive: true, children: [ { name: "Category1Child", isActive: false, } ] }, { name: "Category2", isActive: false, }, { name: "Category3", isActive: true, children: [ { name: "Category3Child", isActive: false, } ] } ];
const inactive = findInactive(categoriesData)
// the following is neeeded if it's possible for a
// node to have children and be inactive
// .map(({name, isActive}) => ({name, isActive}))
;
console.log(inactive);
//setDisabledCategories(inactive); // one time in React
This makes the code a lot easier to reason about and decouples React's API out from the filtering logic, which can be moved out to a generic function agnostic of React.
As others have mentioned, if you do want to call setState multiple times as a batch update, you can use the prevState callback to chain the updates: setDisabledCategories(prevState => [...prevState, category]);.

Select the table rows by default based on the data in antd table

I'm new to antd and I'm stuck at one place in my project. I want to check/select the checkboxes of rows by default based on the sourcedata or data.
for example if i have the datasource as
const data =
[
{
key: "1",
name: "John",
age: 22,
chosen: true,
working: null
},
{
key : "2",
name: "Jason",
age: 23,
chosen: false,
working: 'John'
}]
So based on datasource if any object has chosen key as true, I want to check/select the checkbox of those rows by default.
I can filter out the data array depending on the chosen key has the value true or not. But how to check the checkbox by default? Is there any prop for the antd table, which will take the array of filtered data and check/select the checkbox for those rows?
I tried to check the rows using checked attribute inside getCheckboxProps but when I use that in console I get a warning saying "Warning: [antd: Table] Do not set checked or defaultChecked in getCheckboxProps. Please use selectedRowKeys instead."
Below is the code which I'm currently using.
const data =
[
{
key: "1",
name : "John",
age : 22,
chosen: true,
working: null
},
{
key : "2",
name: "Jason",
age: 23,
chosen: false,
working: "John"
}
]
const fiterSelectedRows = data.filter(row => {
return row.chosen;
});
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(
`selectedRowKeys: ${selectedRowKeys}`,
"selectedRows: ",
selectedRows
);
},
getCheckboxProps: record => {
return {
disabled: record.working != null,
name: record.working
};
}
};
<Table rowSelection={rowSelection} columns={columns} dataSource={data}/>
Notice selectedRowKeys: data.filter(item => item.chosen).map(item => item.key). selectedRowKeys contain all keys of items, all items have keys in here will be checked by default.
You need to get all items that have chosen is true.
data.filter(item => item.chosen)
// [{key: '1', name: 'John Brown', ...},
// {key: '3', name: 'Joe Black', ...},
// {key: '4', name: 'Joe Black', ...}]
// all items have chosen is true
Then get all keys of this array.
data.filter(item => item.chosen).map(item => item.key)
// ['1', '2', '3']
// all keys of items have chosen is true
Exmample code:
Data
const data = [{
key: '1',
name: 'John Brown',
age: 32,
chosen: true,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
chosen: false,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
chosen: true,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Disabled User',
age: 99,
chosen: true,
address: 'Sidney No. 1 Lake Park',
}];
Datatable
class App extends React.Component {
state = {
selectedRowKeys: data.filter(item => item.chosen).map(item => item.key), // Check here to configure the default column
loading: false,
};
start = () => {
this.setState({ loading: true });
// ajax request after empty completing
setTimeout(() => {
this.setState({
selectedRowKeys: [],
loading: false,
});
}, 1000);
};
onSelectChange = selectedRowKeys => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
};
render() {
const { loading, selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
};
const hasSelected = selectedRowKeys.length > 0;
return (
<div>
<div style={{ marginBottom: 16 }}>
<Button type="primary" onClick={this.start} disabled={!hasSelected} loading={loading}>
Reload
</Button>
<span style={{ marginLeft: 8 }}>
{hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
</span>
</div>
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
</div>
);
}
}
The author above said all the right things, I decided to share my experience. I have an array of objects with no key field (hello backend). Initially I filtered this array by a condition (depends on your task), for example, that the coin_symbol field === "WBNB". After filtering, I immediately use map to create a new array. Don't forget to make local state.
const [selectedRowKeys, setSelectedRowKeys] = useState()
setSelectedRowKeys(dashboardData?.filter(i => i.coin_symbol === 'WBNB').map(i => i.id))
Then, after digging in the table props, I saw a props defaultSelectedRowKeys, which takes an array. Further everything is simple - to this props assign value to our new array.
const rowSelection = { defaultSelectedRowKeys: selectedRowKeys, ...otherProps (like onChange, getCheckboxProps, etc.)
Another important thing - if you have, as I do, the data from the server go long, better not render a table, but rather, for example, some kind of loading text or a slider.
selectedRowKeys ? <YourTable dataSource={yourData} rowSelection={rowSelection}/> : <Loader/>
I hope it helps someone! (~ ̄▽ ̄)~

Reactjs - How can I duplicate a prop?

So I've done some reading on here and saw that most people recommended against modifying props. Thus, I was wondering if there's a way I can duplicate the prop?
Essentially, I want to duplicate the prop and set it to the state. More specifically, I'm creating a table and one of the props that is passed in is the headers which is an array of objects
headers={[
{id: "Name" , numeric: false, disablePadding: false, label: "Name"},
{ id: 'Weight', numeric: true, disablePadding: false, label: 'Weight' },
{ id: 'Height', numeric: true, disablePadding: false, label: 'Height' }
}]
I want to add a default column s.t. it'll look like
headers={[
{id: "Name" , numeric: false, disablePadding: false, label: "Name"},
{ id: 'Weight', numeric: true, disablePadding: false, label: 'Weight' },
{ id: 'Height', numeric: true, disablePadding: false, label: 'Height' },
{id: "Modify" , numeric: false, disablePadding: false, label: "Modify"}
]}
Thanks for your help! :-)
There are a few techniques you could use without using additional libraries
1. Set the initial state properly
this.state = { headers: this.props.headers.slice(0) }
When modifying the state use the callback technique
this.setState((previousState) => {
// mutate the state a return a new one.
});
How to use slice
Using setState properly
Array object is passed by reference. Instead you can create a new array and then dump the data into the state.
this.state = {
headers: (() => {
return [...this.props.headers, {id: "Modify" , numeric: false, disablePadding: false, label: "Modify"}]
})()
}
Create the component which you have to add.
let newObject = { "id": "Modify" , "numeric": false, "disablePadding": false, "label": "Modify" }
Now, create a duplicate along with the newObject which was created.
const headers = [...this.props.headers, newObject]
Now, set that headers to state variable header.
this.setState({
header: headers
})
I hope this will work for you.
Do this in constructor:
this.state: {fooCopy: this.props.foo};
if you want to store modified prop in state, then make a copy in a local var (try Object.assign or JSON.parse(JSON.stringify(a))), modify it, and then store in state.

Resources