Correctly sanitize data for request - Typescript, React

Viewed 480

Background

I'm not sure how I should approach sanitizing data I get from a Java backend for usage in a React form. And also the other way around: sanitizing data I get from a form when making a backend request. For frontend/backend communication we use OpenApi that generates Typescript interfaces and API for us from DTOs defined in Java.

Scenario

Example of the Schema in Java:

public enum Pet {
    CAT,
    DOG
}

@Schema(description = "Read, create or update an account")
public class AccountDto {

    @NotNull
    private Boolean active;

    @NotBlank
    private String userName;
    
    @NotNull
    private Pet preferedPet;
    
    @Nullable
    private String bioDescription;
    
    // Constructor and getter/setters skipped
}

Current implementation

Example of the generated Typescript interface:

enum Pet {
    CAT,
    DOG
}

interface AccountDto {
    active: boolean,
    userName: string,
    preferedPet: Pet,
    bioDescription?: string     // Translates to: string | undefined
}

Example React.js:

import {getAccount, updateAccount, Pet, AccountDto} from "./api"

export default function UpdateAccount() {

    const [formData, setFormData] = useState<AccountDto>({
        active: true,
        userName: "",
        preferedPet: Pet.CAT,
        bioDescription: ""
    })
    
    useEffect(() => {
        async function fetchAccount() {
            const response = await getAccount();
            // Omitted error handling
            setFormData(response.data);
            
            // response.data could look like this:
            //  {
            //      active: true,
            //      userName: "John",
            //      preferedPet: Pet.DOG,
            //      bioDescription: null
            //  }
        }
    }, [])
    
    async function updateAccountHandler() {
        const response = await updateAccount(formData);
        // Omitted error handling
        
        // Example formData object:
        //  {
            //      active: true,
            //      userName: "John",
            //      preferedPet: Pet.CAT,
            //      bioDescription: ""
            //  }
    }
    
    return (
        // All input fields
    )
}

Problems

  • When fetching the account, bioDescription is null. React will throw a warning that a component (bioDescription input) is changing from uncontrolled to controlled.
  • If by any chance there is a situation where null is set for preferedPet we will get a warning that the select value is not valid.
  • When updating the account all empty strings should be null. Required for the database and generally cleaner in my opinion.

Questions

1.) I'm wondering how other React users prepare/sanitize their data for usage and requests. Is there a go to or good practice I'm not aware of?

2.) Currently I'm using the following function to sanitize my data. It seems to work and Typescript does not notify me about any type mismatches but I think it should since bioDescription can only be string | undefined and not null.

function sanitizeData<T>(data: T, type: "use" | "request"): T {
    const sanitizedData = Object.create({});

    for (const [key, value] of Object.entries(data)) {
        if (!value && type === "use") {
            sanitizedData[key] = "";
        } else if (!value && type === "request") {
            sanitizedData[key] = null;
        } else {
            sanitizedData[key] = value;
        }
    }
    return sanitizedData;
}

I have a situation where I'm trying to manually change a prop without using the React setState.

formData.description = null;

At this point Typescript is telling me that null is not possible. That's how I detected that my sanitizer function might not be correct.

Demo

Sandbox - https://codesandbox.io/s/async-cdn-7nd2m?file=/src/App.tsx

0 Answers
Related