react How to change the value of object in react array - reactjs

development enviroment
・ react
・ typescript
state to update the member object in the state array.
I implemented is as follows.  
However, I get the following error and cannot implement it well.
errormessage
Type'(IMemberLavel | {Language: IMember;}) []'cannot be assigned to type'IMemberLavel []'
interface IMemberLevel {
member: IMember;
level: ILevel;
}
interface IMember {
id: number;
name: string;
}
interface ILevel {
id: number;
name: string;
}
interface ISearch {
name: string;
age: number;
groups?: IGroups[];
memberLavel: IMemberLevel[];
}
interface IState {
searchState: ISearch;
text: string,
display: boolean
}
const Index: FC = () => {
const [state, setState] = useState<IState>({
searchState: initialSearch,
text: '',
display: boolean
});
const onClickMember = (member: IMember) => {
setState({
...state,
searchState: {
...state.searchState,
memberLavel: [...state.searchState., { member : member }],
},
});
};
Postscript
const inithialMemberLavel: IMemberLevel = {
member: { id: 0, name: '' },
level: { id: 0, name: '' },
};
const initialSearch: ISearch = {
name: '',
age: 0,
groups: [],
memberLavel: inithialMemberLavel[]
}

The new member you're adding to memberLavel is missing level property, either add the property
setState({
...state,
searchState: {
...state.searchState,
memberLavel: [
...state.searchState.memberLavel,
{ member, level: { id: /* some number */, name: /* some name */ } },
],
},
});
or set it as optional in IMemberLevel interface
interface IMemberLevel {
member: IMember;
level?: ILevel;
}

Related

react add empty object to nested array using typescript

How do I update state of a nested array of object to add any empty object using typescript? I want to add to the bottomlevels in level object
how I want final state to look.
const data = {
level: {
subLevels: [
{
bottomLevels: [
{
...
},
],
},
{
bottomLevels: [
{
//I want to update the data here
},
],
},
],
},
};
types
type LevelDataType = {
id: number;
name: string;
subLevels?: SubLevelDataType[];
};
type SubLevelDataType = {
id: number;
name: string;
bottomLevels?: BottomLevelsDataType[];
subLevelId: number;
};
type BottomLevelsDataType = {
id: number;
name: string;
SubLevelId: number;
};
state
this.state = {
newData: {
level: {} as LevelDataType,
subLevels: [],
BottomLevels: [],
}
}
set state
when I try to go down to a nested array object in bottomLevels to add an empty obj it doesnt let me
this.setState({
newData: {
...this.state.newData,
level: [
...this.state.newData.level,
subLevels:[
...this.state.newData.level.subLevels,
bottomLevels:[
...this.state.newData.level.subLevels.bottomLevels,
{} as bottomLevelsType
]
]
],
},
});

type of custom object in typescript

I'm new to typescript and I created this object:
const initialState = {
title: {
value: "",
error: false
},
amount: {
value: 0,
error: false
},
type: "Food",
time: new Date()
}
I want to use is as initial state of a useState. therefore I want to now what I need to passe as type to my useState.
const [Form, setForm] = useState< "what should come here?" >(initialState);
thanks in advance,
interface IState {
title: {
value: string;
error: boolean;
};
amount: {
value: number;
error: boolean;
};
type: string;
time: Date;
}
const [Form, setForm] = useState<IState>(initialState);
You could do something like this:
interface SomeName {
value: string;
error: boolean;
}
interface IState {
title: SomeName;
amount: SomeName;
type: string;
time: Date;
}
const initialState: IState = {
title: {
value: "",
error: false
},
amount: {
value: 0,
error: false
},
type: "Food",
time: new Date()
}
const [Form, setForm] = useState<IState>(initialState);

Nested Object -> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type

I have a react form component that I'm trying to rewrite to typescript. I am trying to retrieve an object within another object in a for loop but I keep getting the following error
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type
I've attempted the solution presented in the following questions but I was still unable to have a working solution and I do not wish to set noImplicitAny to false in tsconfig.json:
TypeScript: Object.keys return string[]
TypeScript TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'
element implicitly has an 'any' type because type '{0}' has no index signature.
The code in question is - The problematic area can be found near the bottom:
import React from 'react';
import Button from '../../UI/Button/Button';
import Input from '../../UI/Input/Input';
type Config = {
elementType: string;
elementConfig: {
type: string;
placeholder: string;
options: {
value: string;
displayValue: string;
};
};
value: string;
validation: {
required: boolean;
isEmail?: boolean;
minLength?: number;
};
valid: boolean;
touched: boolean;
fieldActive: boolean;
// [key: string]: string | Object;
}
interface SignInFormProps {
isSignIn: boolean;
}
interface SignInFormState {
controls: {
email: Config;
password: Config;
};
isSignUp: boolean;
[key: string]: boolean | Object | Config;
}
class SignInForm extends React.Component<SignInFormProps, SignInFormState> {
state = {
controls: {
email: {
elementType: 'input',
elementConfig: {
type: 'email',
placeholder: 'Your Email',
options: {
value: '',
displayValue: ''
},
},
value: '',
validation: {
required: true,
isEmail: true
},
valid: false,
touched: false,
fieldActive: false
},
password: {
elementType: 'input',
elementConfig: {
type: 'password',
placeholder: 'Password',
options: {
value: '',
displayValue: ''
},
},
value: '',
validation: {
required: true,
minLength: 6
},
valid: false,
touched: false,
fieldActive: false
}
},
isSignUp: true
}
private activateField = ( controlName: keyof SignInFormState['controls'] ) => {
do stuff...
}
...
render() {
const formElementsArray: {id: string, config: Config}[] = [];
// ################ The config value is causing the error ################
for ( let key in this.state.controls ) {
formElementsArray.push({
id: key,
config: this.state.controls[key] as Config
});
}
let form = formElementsArray.map( formElement => (
<Input
blur={ ( event ) => this.disableFocus(event, formElement.id) }
changed={ ( event ) => this.inputChangedHandler(event, formElement.id) }
elementConfig={formElement.config.elementConfig}
elementType={formElement.config.elementType}
fieldActive={formElement.config.fieldActive}
focus={ () => this.activateField(formElement.id) }
invalid={!formElement.config.valid}
key={formElement.id}
shouldValidate={formElement.config.validation.required}
touched={formElement.config.touched}
value={formElement.config.value} />
));
If anybody has any ideas on how to resolve this while having clearly defined types and without using any then that would be helpful.
First of all, there isn't a nice way to do this and it's still in discussion: https://github.com/Microsoft/TypeScript/issues/3500
Below are 2 potential ways to solve your issue.
Declare the key outside of the loop:
const formElementsArray: { id: string; config: Config }[] = [];
let key: keyof typeof this.state.controls;
for (key in this.state.controls) {
formElementsArray.push({
id: key,
config: this.state.controls[key],
});
}
Use Object.keys & cast the key to the desired type:
const formElementsArray: { id: string; config: Config }[] = (Object.keys(
this.state.controls
) as (keyof typeof this.state.controls)[]).map((key) => ({
id: key,
config: this.state.controls[key],
}));
You could also make it clearer by using an enum for the control key
enum ControlKey {
email = "email",
password = "password",
}
interface SignInFormState {
controls: {
[key in ControlKey]: Config;
};
isSignUp: boolean;
[key: string]: boolean | Object | Config;
}
then
1b.
const formElementsArray: { id: string; config: Config }[] = [];
let key: ControlKey;
for (key in this.state.controls) {
formElementsArray.push({
id: key,
config: this.state.controls[key],
});
}
or
2b.
const formElementsArray: { id: string; config: Config }[] = (Object.keys(
this.state.controls
) as ControlKey[]).map((key) => ({
id: key,
config: this.state.controls[key],
}));
Hope this helps

React updating nested arrayelement via setState

I'm trying to set state of input fields.
I want to update specific input value when a key is pressed.
setState state looks fine, but I'm getting error on map function of render part.
(this.state.form[this.state.mode].form.map)
I don't understand why it breaks on map.
export type IMode = "login" | "forgot";
export interface IInput {
value: string;
error: string;
id: string;
name: string;
placeholder: string;
type: "text" | "password";
}
export interface IState {
isRequestPending: boolean;
backendError: string;
mode: IMode;
form: {
[key in IMode]: {
name: string;
method: string;
action: string;
fields: IInput[];
};
};
}
in constructor
this.state = {
isRequestPending: false,
backendError: "",
mode: "login",
form: {
login: {
name: "Login",
method: "",
action: "",
fields: [
{
value: "",
error: "",
id: "test",
placeholder: "Login",
type: "text",
name: "login"
}
]
},
.... and so on
}
};
private handleFormInput = (e, input: IInput, index) => {
this.setState((prevState) => ({
...prevState,
backendError: "",
form: {
...prevState.form,
[this.state.mode]: {
...prevState.form[this.state.mode],
fields: {
...prevState.form[this.state.mode].fields,
[index]: {
...prevState.form[this.state.mode].fields[index],
value: "it Works, but map crash"
}
}
}
}
}
You are converting fields into an object which doesn't contain a map method. Update your state like this
this.setState(prev =>({
/*...*/
fields : prev.form[this.state.mode].fields.map((field, i) =>{
if(i !== index) return field
return {
...field,
value : 'foo'
}
})
}))

React TypeScript: How to spread in other values of a nested state object

When updating a single value in shared, how do I spread in the other existing values
interface StateInterface {
env: {
apiUrl: string;
};
display: {
modal: string;
};
shared: {
avatar: string;
email: string;
fullName: string;
loggedIn: boolean;
language: string;
warningMessage: string;
};
}
const InitialState: StateInterface = {
env: {
apiUrl: 'http://localhost:8888',
},
display: {
modal: '',
},
shared: {
avatar: '',
email: '',
fullName: 'John Doe',
language: 'en',
loggedIn: false,
warningMessage: '',
},
};
const Reducer = (state: StateInterface, action: any) => {
switch (action.type) {
case 'SET_AVATAR':
return {
...state,
shared: {avatar: action.value}, // <<-- Will the other items in shared get overwriten, if so how to spread them in?
};
...
return {
...state,
shared: {...state.shared, avatar: action.value}, // <<-- Will the other items in shared get overwriten, if so how to spread them in?
};

Resources