1

How can I set and existing state field dynamically in a React component?

My current tries lead to the following approach, but it does not work in TypeScript:

export interface MaskCompanyDetailState {
    fieldId: number;
}

export class MaskCompanyDetail extends React.Component<MaskCompanyDetailProps, MaskCompanyDetailState> {

    public constructor(props: any) {
        super(props);

        // Set field defaults.
        this.state = {
            fieldId: 0,
        };
    }

    public componentDidMount() {
        const myField: string = 'fieldId';
        const myVal: number = 123;
        this.setState({[myField]: myVal});
        this.setState({myField: myVal});
    }
}

Error message from TSLint for this.setState({[myField]: myVal});:

Argument of type '{ [x: string]: number; }' is not assignable to parameter of type 'MaskCompanyDetailState | ((prevState: Readonly, props: Readonly) => MaskCompanyDetailState | Pick<...>) | Pick<...>'. Type '{ [x: string]: number; }' is missing the following properties from type 'Pick': fieldId, fieldCompanyName, fieldStreetName, fieldStreetNumber, and 4 more.ts(2345)

Error message from TSLint for this.setState({myField: myVal});:

Argument of type '{ myField: number; }' is not assignable to parameter of type 'MaskCompanyDetailState | ((prevState: Readonly, props: Readonly) => MaskCompanyDetailState | Pick<...>) | Pick<...>'. Object literal may only specify known properties, and 'myField' does not exist in type 'MaskCompanyDetailState | ((prevState: Readonly, props: Readonly) => MaskCompanyDetailState | Pick<...>) | Pick<...>'.ts(2345)

There is already a post here, but I don't see how to apply the suggested IState interface.

4
  • do you want to add a property to the object ? or else do you want to use value of myField variable to dynamically set property name ? Commented Jan 2, 2020 at 4:36
  • Why do you have quotes around the myField key? Kind of defeats the point of a computed property, {['myField']: 123} is the same as {myField: 123}, which I assume isn't what you want to do, I'm guessing myField is a variable, in which case it should be {[myField]: 123} Commented Jan 2, 2020 at 4:38
  • I have updated the post by specifying the initial question and by providing more code. Commented Jan 2, 2020 at 4:41
  • Tangentially related: "TSLint" refers to a now-deprecated separate tool. You probably meant "TypeScript" there. Commented Jan 2, 2020 at 5:59

1 Answer 1

3

Add the following field to MaskCompanyDetailState interface -

[key: string]: any;

i.e.,

interface MaskCompanyDetailState {
    ... // existing fields
    [key: string]: any;
}

It means that you are allowing any property which has a key of type string and value of any type to be set in your state. This is what your linked post says. This is an example of Index Signature which is a feature of Typescript.

It's not really recommended, because it affects readability of your code in the long run and other people in your team may abuse it. But I am not aware of any other solutions to your predicament other than to rethink your design.

Sign up to request clarification or add additional context in comments.

3 Comments

Is it possible to set it readonly so that it is possible to read/write existing field, but to block the possibility to create new fields at runtime?
The [key: string]: any; part is an Index Signature, and not really a property. I have edited my answer to include a link for more information. While you can set it to readonly, when you execute this.setState({[myField]: myVal}); in your code, you are actually creating new fields/properties in the state at runtime. There is no way around that.
readonly means you can only create(and mutate in case of arrays) a property on the state. AFAIK, there is no way to stop people from creating new fields at runtime with an Index Signature. My suggestion would be to declare all possible fields in MaskCompanyDetailState, but mark them as optional, instead of using an Index Signature.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.