Empty nested array when initializing state object - reactjs

I've the following state initialised in a react app:
constructor(props) {
super(props);
this.state = {
meetings: [],
}
}
I need this.state.meetings to contain { Meetings: [] } until it is populated based on user action. How can i set this before the page renders?
The reason is that i have map functions running on this.state.meetings.Meetings and so i get an undefined error, which makes sense if the default is []. Rather than handle this in the code i wonder if i can just set Meetings: [] as a default value?

I would highly discourage you to store it as a nested structure. Keep it as flat as possible. So instead of directly setting the nested field Meetings inside state, just push or replace the data received from the api in the meetings field.
yourApiCall.then((response) => { // some pseudo code
this.setState({ meetings: response.Meetings });
});

Yeah, it's relatively simple, simply do
constructor(props) {
super(props);
this.state = {
meetings: {
Meetings: []
},
}
}
p.s. it also isn't good practice to have your object parent name be the same as a child :)

You can either do this.state = { meetings: { Meetings: [] } } because javascript is type free, this.state.mettings can assign to a [] later on.
Or a safer way:
// if meetings.Meetings is undefined, use empty array
const Meetings = this.state.meetings.Meetings || [].
//use Meetings to do stuff

Related

How to Setstate array in React Native

How to set state field in array.
Below is my code:
constructor() {
super();
arrayCheck: [
isCheck = false
]
}
I want to get isCheck and then set state to true.
How can I set it?
Below is my function setState
onCheckBoxChange(isCheck, index, item) {
this.setState({........});
}
If you really want to update a state variable using array you can try this.
First, you need an array of object in state variable and update it like this
constructor () {
super();
this.state = {
arrayCheck: [
{ isCheck : false }
]
}
}
then update its value like this
onCheckBoxChange(isCheck, index, item) {
let arrayCheck = [...this.state.arrayCheck]
arrayCheck[0].isCheck = true
this.setState({
arrayCheck
})
}
First you need arrayCheck to be in the state object:
this.state = {
arrayCheck = [...]
}
Second, I would suggest using an object rather than an array. In array you can only access the index that holds the value.
With an object you may do something like:
constructor () {
super();
this.state = {
objectChecks: { // my also just set isCheck on the state
isCheck: false
}
}
}
And then when you want to change it:
this.setState({objectChecks: {isCheck: true}})
Hope it helps

react-native: How to setState() dynamic state in react-native?

I can create dynamic state like this example:
constructor(props) {
super(props);
this.state = {};
}
create state with this method:
func(name){
this.state[name];
}
and setState with this:
func2(name,value){
this.setState({[name]:value});
}
so with
this.func('color');
this.func('size');
I have this.func.color and this.func.size. right?
It works.
But I want something like this. I want create all new dynamic state in 'names' state.
constructor(props) {
super(props);
this.state = {names:[]};
}
names is a normal state.
func(name){
this.state.names[name];
}
func2(name,value){
this.setState({names:{[name]:value}:});
}
I call this functions:
func('color');
func('size');
func2('color','red');
func2('size','larg');
I expect with console.log(this.state.names) this:
{color:'red',size:'larg'}
But I get only {size:'larg'} (second function)
what is my wrong?
You're overwriting the value of names when you call this.setState again.
You're effectively doing:
this.setState({ names: { color: 'red' }});
this.setState({ names: { size: 'large' }});
Consider using Object.assign() in func2 to make sure you're not replacing the object you're trying to add properties to.
func2(name,value) {
this.setState({
names: Object.assign(this.state.names, {[name]: value})
});
}

React setting default state

Im looking for some guidance in terms of setting the default state in react.
I have a simple project using typescript & react that allows files to be dropped onto a div.
I'm trying to store these files in the state but I'm having trouble setting the default state.
export interface IAppState{
value:string;
droppedFiles: FileList
}
constructor(props: {}) {
super(props);
this.state{
value:'',
droppedFiles?:???
}
}
public render(): JSX.Element {
return (
<div className="App">
<div className="Padding">
<FileDrop onDrop={this.handleDrop}>
Drop some files here!
</FileDrop>
</div>
</div>
);
}
private handleDrop = (files:FileList, event:ReactDragEvent<HTMLDivElement>) => {
this.setState({
droppedFiles:files
});
console.log(this.state.droppedFiles);
}
If the droppedFiles is removed from the this.state step it obviously flags up as can't find the value.
due to TypeScript's type safety, it won't accept {}. What's the proper way to initialize / set the default values for a complex type?
My main issue is that I'm faced with an error from typescript because I don't know how to initialize the state.droppedFiles property with the correct data. I don't want to assign any data to it in the constructor, I'll do that when the user actually drops files.
I'm just looking for the correct way to let TS know that the state has a property of 'droppedFiles' which is of type 'FileList':
TS Error
Without this part it throws an error at runtime which I would expect:
Error
So to close it off, It turns out that FileList isn't a File[].
to get around this I updated my Interface:
export interface IAppState{
droppedFiles: File[];
}
I initialized it in my constructor like so:
constructor(props: {}) {
super(props);
this.state = {
droppedFiles: []
}
}
and finally to update my state I extracted each item from the FileList:
private handleDrop = (files:FileList, event:ReactDragEvent<HTMLDivElement>) => {
// Create an array from the FileList (as this is an array-like object)
const filesArr: File[] = Array.from(files);
// Update the state with our new array
this.setState({
droppedFiles: filesArr
});
}
This has sorted my issue. Thanks for putting me on the right path estus. Kudos.
The question cannot be extrapolated to any case. In this specific case if initial value for a property cannot be provided, a property should be made optional:
export interface IAppState{
value:string;
droppedFiles?: FileList
}
And this implies that in places where droppedFiles is used it can be either FileList or undefined, it may require if (this.state.droppedFiles) ... checks, etc.
Another option in this specific case is to not use FileList object (which is array-like) and store an array of File objects instead:
export interface IAppState{
value:string;
droppedFiles: File[]
}
...
this.state = { droppedFiles: [], ... }
...
this.setState({ droppedFiles: Array.from(files) });

Uncaught TypeError: Cannot read property 'Selected' of null on array setState

Im trying to update my state which is an array by pushing new items to it but it constantly keeps failing.
export interface IState{
Selected: any[]
}
export class MyComp extends React.Component<{}, IState>{
constructor(props){
super(props){
this.state = {
Selected: []
}
}
}
clickevent(newItem){
this.setState({
Selected: [...this.state.Selected, newItem]
})
OR
this.setState({
Selected: this.state.Selected.concat(newItem)
})
OR
let newArray = this.state.Selected.slice();
newArray.push(newItem)
this.setState({
Selected: newArray
})
}
}
All of the above method to push data into the array by updating state results in
Uncaught TypeError: Cannot read property 'Selected' of null.
It looks like you forgot to bind clickevent.
constructor(props) {
super(props){
this.state = {
Selected: []
}
this.clickEvent = this.clickEvent.bind(this)
}
You have your state initialised incorrectly in the constructor. Apart from this you need to bind clickEvent function so that it refers to the correct context,
constructor(props){
super(props); // Super doesn't have a function body
this.state = { // no `()` needed here since this.state is an object
Selected: []
}
this.clickEvent = this.clickEvent.bind(this);
}
Check this question:
Uncaught TypeError: Cannot read property 'state or props' of undefined
When you update state based on previous state you should use the callback setState((prevState) => {}) method. Like this
this.setState((prevState) => ({
Selected: [ ...prevState.Selected, newItem]
}))
Here is explanation why is that from the docs
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.

Is Initializing state with props object causes mutation?

In my React application, one of the components needs state initialization from props.
Class ComponentA extends React.Component{
constructor(props){
this.state = {
objectA: props.objectA
}
}
someOnclickFunction(e){
let updatedObjA = this.state.objectA;
updatedObjA.value = e.target.value;
this.setState({
objectA: updatedObjA
})
}
}
In the above code snippet, props.objectA reference is copied to state. So, Am I mutating the props indirectly by updating the state?
Or setState() function will clone the object and keep new reference for the objectA?
class ComponentA extends React.Component {
constructor(props) {
super(props);
// state is null at this point, so you can't do the below.
// this.state.objectA = props.objectA
// instead, initialize the state like this:
this.state = {
objectA: props.objectA,
};
}
someOnclickFunction(e) {
// you can't set "objectA.value" like the following
// this.setState({
// objectA.value: e.target.value
// });
// you need to create a new object with your property changed, like this:
this.setState({
objectA: Object.assign({}, this.state.objectA, { value: e.target.value }),
})
}
}
This is a mistake that many beginners at react make. You can't simply update sub-properties of an object without consequences.. The following would also be wrong:
someOnclickFunction(e) {
var tmp = this.state.objectA;
// WRONG: don't do this either, you're modifying the state by doing this.
tmp.value = e.target.value;
this.setState({
objectA: tmp,
});
}
To elaborate on the correct way to do this using Object.assign.. this function takes all the parameters and merges them into the first element. So by providing a new object as the first parameter, you've created a copy of your first object with your new property.
Object.assign({}) // = {}
Object.assign({}, objectA) // = copy of objectA
Object.assign({}, objectA, { value: "newValue" }) // = copy of objectA with 'value' = 'newValue'.
Note: Object.assign() is a shallow clone, not a deep clone.

Resources