lundi 29 avril 2019

React TypeScript - State should never be exported?

I have a React App that gets asynchronously its info with the method getInitialInfo. The MessageView is the uppermost component, so, the Data should live in its state, even if it will not change. In a perfect world, the IMessageInfo would be passed through props.

We need to export the type IMessageInfo because there is code that depends on this interface.


OPTION 1 - (Flat solution with no private State)

import * as React from 'react';
import Message from './Message';

// IMessageInfo needs to be exported because there is code that depends on it
export interface IMessageInfo {
    message: string;
    icon: string;
}

export interface IMessageViewProps {
    getInitialInfo(): Promise<IMessageInfo>;
}

// MessagesView is the upper most Component 
class MessageView extends React.Component<IMessageViewProps, IMessageInfo> {
    constructor(props) {
        super(props);
        this.state = {
            message: '',
            icon: ''
        };
        this.getInitialInfo();
    }

    private async getInitialInfo(): void{
        let info = await this.props.getInitialInfo();
        this.setState(info);
    }

    render(): JSX.Element {
        // Message is reusable component that receives the info through `props`
        return <Message {...this.state} />);
    }
}

From the React's design perspective the State must be private to the component. I agree with that. But here, all the state info is Public. what can throw this concept way. (If the DATA is always managed in a presenter for example, why should it be private in this case?)


OPTION 2 - (Flat solution, having a private replicated interface for State)

Talking with some colleagues, they argue that we should always keep the state private. I immediately thought about creating a IMessageViewState that would be exactly the same as IMessageInfo. This way, it gets conceptually right but we would get a maintenance issue (IMessageInfo and IMessageViewState to update whenever some of its members change).


OPTION 3 - (Compose IMessageInfo into IMessageViewState. Has a private state)

So, my colleagues suggested to define IMessageViewState as:

interface IMessageViewState {
    messageInfo: IMessageInfo;
}

This way we are favoring composition (they say). But I don't see any advantage to have composition here. Do you see any? For example, if any member of the IMessageInfo changes (message or icon), we would need to pass all the object messageInfo to the this.setState(...), instead of only updating the icon for example. Basically, it would be a more error-prone implementation.


OPTION 4 - (Extend IMessageInfo. Has a private state)

I also thought about having IMessageViewState extending IMessageInfo. It seems the best solution to accomplish a state that is not exported. But my colleagues said that it's not a good solution because we are giving priority inheritance over composition.

I think that inheritance doesn't bring any throwback in here.


CONCLUSION

In my opinion, the Option 1 is the one that best fits the problem. Since all members of the State are public, I think there's no need to have a private State. The Option 1 keeps the code cleaner.

Although, if I were to choose a solution with a private State, the Option 4 would fit better.

QUESTION: What solution would be more correct?

Aucun commentaire:

Enregistrer un commentaire