CRUD sencillo

Vamos a hacer un CRUD para la siguiente ‘base de datos’

{
  "notes": [
    {
      "id": 1,
      "content": "HTML is easy",
      "date": "2023-01-03T10:30:11.414Z",
      "important": false
    },
    {
      "id": 2,
      "content": "Browser can execute only JavaScript",
      "date": "2019-05-30T18:39:34.091Z",
      "important": false
    },
    {
      "id": 3,
      "content": "GET and POST are the most important methods of HTTP protocol",
      "date": "2023-01-09T09:29:26.131Z",
      "important": true
    },
    {
      "content": "Cchuetes",
      "date": "2022-12-18T20:25:10.598Z",
      "important": true,
      "id": 4
    },
    {
      "id": 5,
      "content": "Comprar pan",
      "date": "2023-01-09T08:46:37.794Z",
      "important": false
    },
    {
      "content": "Nueva nota",
      "date": "2023-01-09T09:29:21.122Z",
      "important": false,
      "id": 6
    }
  ]
}

De momento solo gestionamos el contenido. La fecha la ponemos automáticamente y si es importante o no lo dejamos de lado, ponemos siempre ‘false’.

CRUD comentado

import React, { useState, useEffect } from 'react';
import axios from 'axios';

const App = () => {
    // Estado de los posts. Aquí cargaremos la información de la api
    // Parecido a la de tareas
  const [posts, setPosts] = useState([]);
  // Para el formulario, en este caso todos los campos dentro de un objeto
  const [nuevoPost, setNuevoPost] = useState({ title: '', body: '' });

  // Esto se ejecuta la primera vez
  useEffect(() => {
    // Llamamos a la función que carga los posts
    obtenerPosts();
  }, []);

  // Aquí tenemos tres funciones porque son las tres operaciones que podemos hacer:
  // Leer, añadir y eliminar.
  // Es mejorable, pero funciona

  // Llamamos con axios a la api y ponemos lo obtenido en el estado
  const obtenerPosts = async () => {
    try {
      const response = await axios.get('http://localhost:4000/posts');
      setPosts(response.data);
    } catch (error) {
      console.error('Error al obtener posts:', error);
    }
  };

  // LLamamamos a la api con POST y los datos del formulario
  const agregarPost = async () => {
    try {
      await axios.post('http://localhost:4000/posts', nuevoPost);
      // Ponemos a cero el formulario
      setNuevoPost({ title: '', body: '' });
      // Cargadmos los datos de la api (podríamos hacerlo directamente en el estado)
      obtenerPosts();
    } catch (error) {
      console.error('Error al agregar post:', error);
    }
  };

  // Llamamos a la api con DELETE
  const eliminarPost = async (id) => {
    try {
      await axios.delete(`http://localhost:4000/posts/${id}`);
      // Cargamos los datos de nuevo
      obtenerPosts();
    } catch (error) {
      console.error('Error al eliminar post:', error);
    }
  };

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {/* Lista de todos los posts con su 'key */}
        {posts.map((post) => (
          <li key={post.id}>
            <h2>{post.title}</h2>
            <p>{post.body}</p>
            <button onClick={() => eliminarPost(post.id)}>Eliminar</button>
          </li>
        ))}
      </ul>
      {/** Formulario, el change no llama a ninguna función, directamente cambia el estado en
       * los dos campos. Yo prefiero hacerlo con una sola función.
       */}
      <h2>Agregar nuevo post</h2>
      <input
        type="text"
        value={nuevoPost.title}
        onChange={(e) => setNuevoPost({ ...nuevoPost, title: e.target.value })}
        placeholder="Título"
      />
      <textarea
        value={nuevoPost.body}
        onChange={(e) => setNuevoPost({ ...nuevoPost, body: e.target.value })}
        placeholder="Cuerpo"
      />
      <button onClick={agregarPost}>Agregar post</button>
    </div>
  );
};

export default App;

db.json

{
  "posts": [
    {
      "id": "1",
      "title": "Post 1",
      "body": "Cuerpo del post 1"
    },
    {
      "id": "29d4",
      "title": "Post 3",
      "body": "holi"
    },
    {
      "id": "e4da",
      "title": "dfsdfsdfsd",
      "body": "asdasd"
    }
  ]
}

Ejemplo useref

import React, { useRef } from "react";

function App() {
  // Crear una referencia con useRef
  const inputRef = useRef(null);
    
  // Función para obtener el valor actual del input
  const handleGetValue = () => {
    alert(`Valor actual del input: ${inputRef.current.value}`);
  };

  // Función para cambiar el valor del input
  const handleChangeValue = () => {
    inputRef.current.value = "Nuevo valor dinámico";
    alert("El valor del input ha sido cambiado");
  };

  return (
    <div>
      <h1>Ejemplo de obtener y cambiar valor con useRef</h1>
      {/* Input con referencia */}
      <input ref={inputRef} type="text" placeholder="Escribe algo aquí..." />
      <br />
      {/* Botón para obtener el valor */}
      <button onClick={handleGetValue}>Obtener valor</button>
      {/* Botón para cambiar el valor */}
      <button onClick={handleChangeValue}>Cambiar valor</button>
    </div>
  );
}

export default App;

Ejercicio Lista de la compra

Vamos a hacer un ejercicio para mantener una lista de la compra, sin API

Tenemos un campo de texto y un botón de añadir. Si escribimos algo y le damos a añadir lo veremos en una tabla debajo de ese botón.

Cada elemento de la lista de la compra tendrá un botón al lado para eliminarlo

Una cosa como esta:

Producto: [_________] [Añadir]
Lista
Patatas [Borrar]
Tomates [Borrar]
Bacon [Borrar]

Pasos a realizar:

¿Cuál es nuestro estado?
¿Qué componentes vamos a tener?
¿Cómo creamos nuestro ‘árbol’ y como intercambiamos la información?

Ejemplo contexto genérico

App

import React, {  useState } from "react";

import ContextoCon from "./ContextoCon";
import IncrementButton from "./IncrementButton";
import DisplayCount from "./DisplayCount";

function CounterProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <ContextoCon valor={{ count, setCount }}>
      {children}
    </ContextoCon>
  );
}
function App() {
  return (
    <CounterProvider>
      <DisplayCount />
      <IncrementButton />
    </CounterProvider>
  );
}

export default App;

Contexto

import { createContext } from 'react'
const Contexto = createContext({})

export default Contexto;

ContextoCon

import Contexto from "./Contexto";

const ContextoCon= ({children,valor}) => {
    
    return   <Contexto.Provider value={valor}>
                {children}
            </Contexto.Provider>
}

export default ContextoCon;

IncrementButton

import  { useContext } from "react";
import Contexto from "./Contexto";
function IncrementButton() {
    const { count, setCount } = useContext(Contexto);
    return <button onClick={() => setCount(count + 1)}>Incrementar</button>;
  }
export default IncrementButton;

DisplayButton

import  { useContext } from "react";
import Contexto from "./Contexto";
function DisplayCount() {
    const { count } = useContext(Contexto);
    return <p>Contador: {count}</p>;
  }

export default DisplayCount;

Ejercicio mostrar Posts

App

import React, { useState, useEffect } from "react";
import Post from "./Post";
import 'bootstrap/dist/css/bootstrap.min.css';
function App() {
    const [data, setData] = useState(null);
  
    useEffect(() => {
      fetch("https://jsonplaceholder.typicode.com/posts")
        .then((response) => response.json())
        .then((json) => setData(json));
  
      return () => {
        console.log("Limpieza de efecto (si aplica)");
      };
    }, []); // Solo se ejecuta una vez, al montar el componente.
    const removePost=(id)=>{
        setData(data.filter(post=>post.id!=id))
    }
    return (
      <div>
        {data ? <table class="table table-striped">
        <thead>
          <tr>
            <th>Id</th>
            <th>Title</th>
            <th>Body</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
            {data.map((post)=>
               
                <Post key={post.id} post={post} remove={removePost}/>
               )}
        </tbody>
        </table>
        : <button class="btn btn-primary">
        <span class="spinner-border spinner-border-sm"></span>
        Cargando...
      </button>}
      </div>
    );
  }
  
  export default App;

Post

const Post= ({post,remove}) => {
    return <tr>
        <td>{post.id}</td>
        <td>{post.title}</td>
        <td>{post.body.slice(0,20)}...</td>
        <td><button class="btn btn-danger" onClick={()=>{remove(post.id)}}>Borrar</button></td>
    </tr>
}

export default Post;

Ejercicios React

En el juego de los 100 botones vamos a hacer lo siguiente:

Tener un estado con las variables ‘numActual’ y ‘finalizado’. numActual sera 1 y finalizado falso
En los botones llamaremos a una función del padre (como en el ejemplo de trifulcas)
En esa función tenemos que comprobar si el número pulsado es el correcto. Si el número que me pasa el hijo es igual al actual, si es así sumamos 1 al actual.
Si no es así, finalizado true
Si el número es 100 finalizado true

Si alguien quiere tirar de renderizado condicional y si finalizado es true mostrar ‘juego acabado’ y si no los números


Quiero una página que tenga tres botones, cada uno con un nombre de un color y al pulsar el botón en un

me salga ‘Mi color preferido es el ‘ y el color pulsado.


Minicalculadora:
Dos cajas de texto para poner números. Cuando cambien los valores que debajo salga el valor de la suma y el de la multiplicación.

Ejemplo eventos

Boton


const Boton = ({numero,llamada}) => {
    {/* Llamo a la función que me pasan con el número del botón. Esto lo que hace es llamar
        a la función del padre con el valor del botón que se ha pulsado
        Estoy pasando información del hijo al padre */}
    return <button onClick={()=>llamada(numero)} className="btn btn-info">{numero}</button>;
  }
export default Boton;

App

import logo from './logo.svg';
import './App.css';
// Componente importado desde un archivo
import Despedida from './Despedida';
import Boton from './Boton';
import 'bootstrap/dist/css/bootstrap.min.css';
const generateRandomNumbers = () => {
  const arr = Array.from({ length: 100 }, (_, i) => i + 1); // Números del 1 al 100
  for (let i = arr.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [arr[i], arr[j]] = [arr[j], arr[i]]; // Intercambiar elementos
  }
  return arr;
};
const getNumber=(numero)=>{
  console.log(numero)
}
const saludo=(nombre)=>{
  console.log(nombre)
  console.log("Hola "+nombre+" que tal");
}
// Componente Más o menos una función que nos devuelve una mezcla de HTML y JS
function App() {
  // Creo un array de nombres
  const numeros=generateRandomNumbers();
  
  return (
    <div className="App">
       {/* Paso al hijo una función para que se ejecute en un evento suyo */}
        {numeros.map((numero,i)=>
          <Boton numero={numero} key={i} llamada={getNumber}/>
        )}
        <Despedida nombre="Juan"/>
        {/* Aquí llamo a la función sin parámetros y nos pasará el evento */}
        <button onClick={saludo} >Pinchame</button>
        {/* Aquí uso la función flecha para pasar un parámetro propio */}
        <button onClick={()=>saludo("Ana")} >Pinchame</button>
    </div>
   
  );
}

// Exportamos APP (y lo renderizamos en index.js)
export default App;

Diferencias JSX yHTML

HTML y JSX tienen similitudes, ya que JSX (JavaScript XML) está inspirado en HTML, pero existen diferencias clave que los distinguen.

1. Sintaxis

  • HTML: Es un lenguaje de marcado independiente que utiliza una sintaxis estricta de etiquetas HTML.
    <div class="container">
        <h1>Hello, World!</h1>
    </div>
    
  • JSX: Es una extensión de sintaxis para JavaScript que permite escribir estructuras similares a HTML dentro del código JavaScript. Es procesado por herramientas como Babel para convertirse en código JavaScript puro.
    <div className="container">
        <h1>Hello, World!</h1>
    </div>
    

2. Atributos

  • HTML: Utiliza atributos estándar del DOM, como class o for.
    <label for="input">Name:</label>
    <input class="form-control" />
    
  • JSX: Algunos atributos se renombraron para evitar conflictos con palabras clave de JavaScript, como:
    • classclassName
    • forhtmlFor
    <label htmlFor="input">Name:</label>
    <input className="form-control" />
    

3. Expresiones

  • HTML: No soporta expresiones de JavaScript dentro del marcado.
    <p>This is plain HTML.</p>
    
  • JSX: Permite incluir expresiones de JavaScript dentro de llaves {}.
    const name = "John";
    <p>Hello, {name}!</p>
    

4. Cierre de etiquetas

  • HTML: Algunas etiquetas, como <img> o <input>, no requieren cierre explícito.
    <img src="image.jpg" alt="Image">
    
  • JSX: Todas las etiquetas deben cerrarse explícitamente, incluso las vacías.
    <img src="image.jpg" alt="Image" />
    

5. Comentarios

  • HTML: Usa <!-- ... --> para los comentarios.
    <!-- This is a comment -->
    
  • JSX: Los comentarios deben estar dentro de llaves y utilizar la sintaxis de comentarios de JavaScript.
    {/* This is a JSX comment */}
    

6. Contexto de ejecución

  • HTML: Es un lenguaje de marcado estático, interpretado por el navegador directamente.
  • JSX: Es un lenguaje intermedio que se compila a JavaScript antes de ser interpretado por el navegador.

7. Interactividad

  • HTML: Depende de JavaScript externo para manejar eventos e interactividad.
    <button onclick="handleClick()">Click Me</button>
    
  • JSX: Permite manejar eventos directamente con funciones de JavaScript.
    <button onClick={handleClick}>Click Me</button>
    

En resumen, mientras que HTML es un lenguaje de marcado puro, JSX es una extensión de JavaScript diseñada para React que combina la apariencia de HTML con la potencia de JavaScript, lo que lo hace más dinámico y flexible.