Digamos que tu backend está escrito en Django o en Rails. Los datos que obtienes y envías tienen que estar en snake_case porque esa es la convención de estos frameworks.

Pero cuando manejas estos datos en tu aplicación React, usas JavaScript donde la convención es camelCase...

¿Cómo solucionas esto sin hacer que tu Linter se enoje? ¿Será necesario romper la convención para Rails o para React?

Si no te importa romper las convenciones, anda y usa snake_case en tu app React (lo siento Linter! 💔)

Para el resto, les tengo una solución simple:

  • después de obtener datos de tu backend, transfórmalos a camelCase.
  • antes de enviar datos a tu backend, transfórmalos a snake_case.

¿Por qué hacer esto en el cliente? Porque el 99% de las veces harás una función o un wrapper que haga los llamados por ti. Por ejemplo, para transformar las respuestas en JSON, o para agregar tókenes de autenticación. Así que basta con extenderlo un poco más.

El código

La transformación en sí es fácil porque hay librerías que lo hacen. En el ejemplo, usaré humps, el cual es un convertidor de Underscore a camelCase (y vice versa) para strings y objetos en JavaScript.

después de obtener datos de tu backend, transfórmalos a camelCase.

Básicamente:

// api.js

import humps from 'humps';

export async function get(url) {
    return fetch(url)
        .then(response => response.json())
        .then(json => humps.camelizeKeys(json))
}
antes de enviar datos a tu backend, transfórmalos a snakeCase
// api.js

import humps from 'humps';

export async function post(url, data) {
    const body = JSON.stringify(humps.decamelizeKeys(data));
    return fetch(url, { method: 'POST', body })
        .then(response => response.json())
        .then(json => humps.camelizeKeys(json))
}

Luego en tus components React usa estas funciones y listo!

Ejemplo de uso

App demo

Aquí está el código de una app ejemplo que usa PokeApi:

import React, { useState, useEffect } from "react";
import "./styles.css";
import { get } from "./api";

function PokemonCard({ name }) {
  const [pokemon, setPokemon] = useState(null);

  useEffect(() => {
    get(`/pokemon/${name}`).then(data => setPokemon(data));
  }, [name]);

  if (!pokemon) return null;

  const src = pokemon.sprites.frontDefault; // camelCase :D

  return (
    <div
      style={{
        margin: 10,
        width: 96,
        height: 96,
        display: "inline-block",
        backgroundImage: `url(${src})`
      }}
    />
  );
}

export default function App() {
  const [pokemons, setPokemons] = useState([]);

  useEffect(() => {
    get("/pokemon?limit=150").then(data => setPokemons(data.results));
  }, []);

  return (
    <div className="App">
      <h1>Pokemons!</h1>
      <div>
        {pokemons.map(({ name }) => (
          <PokemonCard key={name} name={name} />
        ))}
      </div>
    </div>
  );
}
Ejemplo usando API wrapper

¿Por qué funciona?

  • La mayoría de las apps en React necesitan algún tipo de wrapper para realizar requests. Puede ser para transformar las respuestas en JSON, para agregar token de autenticación, etc. Entonces, extender el wrapper un poco más para transformaciones está bien y es simple de realizar.
  • A veces no podrás tocar tu código de backend. Así que en cualquier caso la transformación tiene que ser en React.