Índice del curso de ReactJS

  1. Instalación y configuración inicial
  2. Estructura básica de un proyecto con ReactJS
  3. JSX
  4. Recogida de datos
  5. State
  6. Componentes
  7. Props
  8. Renderizado condicional
  9. Arrays
  10. Style
  11. Valor por defecto de una combo
  12. React router
  13. Fetch API
  14. Context API
  15. Login
  16. Redux
  17. React Developer Tool
  18. Sockets
  19. Build
  20. TypeScript
  21. React Native
  22. Expo
  23. React native
  24. Navigation
  25. Compilar con Expo en la nube
  26. Create React App
  27. Compartir código entre React Web y React Native. Dos aproximaciones:
    1. Empaquetar con NPM
    2. Importar código desde fuera del proyecto pero en el propio ordenador
    3. Importar código con prototype

Redux

Permite definir estados globales.

No tiene sentido usar Redux y useState para una misma variable. Si una variable es global a nivel de aplicación, no tiene sentido hacerla global a nivel de componente. Estaríamos guardando la aplicación dos veces.

Necesitaremos instalar el siguiente módulo para poder usarlo:

npm i redux
npm i react-redux 

Diferencia entre Context API y Redux

Redux es una librería totalmente externa a react.

  • Reducers
  • Actions
  • Middlewares

Context API

  • Provider value={useRreducer()}
  • Consumer

Aplicación con Redux

El objeto en el que se guarda el state global de la aplicación se llama store.

Estructura global:

  • src/App.js
  • src/components/
    • form/form.js
    • palabra/palabra.js
  • src/store/
    • palabra
      • actions.js
      • reducer.js (modifican los estados de la aplicación)
    • index.js (aquí se registran los reducers)
./src/App.jsimport React, { Component } from 'react';
import Form from './components/Form';
import PalabraAlmacenada from './components/palabra';

const App = _ =>(
	<div className="App">
		<Form/>
		<PalabraAlmacenada/>
	</div>
);

export default App;

Los componentes

Un componente que accede a un estado de Redux, debe recibirlo como prop. Si no lo hacemos, el código no dará error, pero tampoco funcionará.

./src/components/form/form.jsimport { modificaPalabra } from '../../store/palabra/actions';
import {connect} from "react-redux";

const Form = ({ modificaPalabra }) => {
	const palabraRef = useRef(null);
	return (
		<input ref={palabraRef} type="text" placeholder="La palabra"/>
		<button onClick={ () => {modificaPalabra(palabraRef.current.value);}} >Add</button>
	)
};

/* - El método connect conecta un componente (en este caso Form) con la Store.
   - El primer parámetro (que en este caso vale null porque no lo necesitamos) es la función de este componente que recibiŕía el state de la store para pintarlo por pantalla.
   - El segundo parámetro es un objeto que contiene las acciones de redux que vamos a utilizar en el componente.
Tanto el state al que nos hemos suscrito con el primer parámetro, como las acciones del segundo le llegarán al componente como props.*/

export default connect(null, { modificaPalabra })(Form)

//Si fuesen varios métodos a los que nos quisieramos conectar, lo haríamos de esta forma:
//export default connect(null, { modificaPalabra, modificaOtraCosa })(Form)

El siguiente comoponente no modifica la store, por tanto, no tengo que vincularlo a un action.

./src/components/palabra/palabra.jsimport React from 'react';
import {connect} from 'react-redux';
import { selectActiveWord } from '../../store/palabra/reducer';

const PalabraAlmacenada = ({ palabra }) => (
	<output>{palabra}</output>
);

/*
* Cada vez que se ejecuta una acción, se ejecutará también una llamada a todas las funciones mapStateToProps de mi aplicación
* Por tanto, usaremos esta función para recuperar la parte del esta que nos interesa.
* */
function mapStateToProps(state) {
    return {
        palabra: selectActiveWord(state)
    }
}

/*La función connect, vincula el componente PalabraAlmacenada con la store.
Para ello, utilizamos la función mapStateToProps cuyo parámetro state representa la store. 
En este caso no usamos acciones (el segundo parámetro).*/

export default connect(mapStateToProps)(PalabraAlmacenada);

actions

La modificación de los estados del store no se hace directamente. Para efectuarla, tendremos que definir y usar unas funciones llamadas actions.

Estas funciones tienen una estructura específica. Deben devolver un objeto con las siguientes propiedades:

  • type. Esta propiedad es obligatoria y su valor debe ser un string. Es la propiedad que usaremos en el reducer para determinar el cambio concreto a realizar en la store.
  • payload. Esta propidad es opcional. Es un contenido asociado a la acción que vamos a realizar. Por ejemplo, si queremos dar una persona de alta, sería un objeto con las propiedades de esa persona.
./src/store/palabra/actions.js// Las acciones siempre tienen la estructura { type, payload }
export const modificaPalabra = (palabra) => {
    return {
        type: 'MODIFY_WORD',
        payload: palabra
    }
};

reducers

Se encargan de materializar los actions.

Cuando lanzamos un action, se ejecutan todos los reducers, hasta encontrar aquel cuya condición coincide con el type de la action que estamos ejecutando.

Incluso aunque ya se haya cumplido la igualdad del type, seguirán ejecutándose todos los reducers hasta llegar al final.

./src/store/palabra/reducer.jsconst initialState = { palabra: "" };

// action es el valor devuelto por el action
//action.payload será el valor que quiero añadir, borrar, etc
export default (state = initialState, action) => {
    if (action.type === 'MODIFY_WORD') {
        return {
            ...state, //Lo que devuelve un reducer es lo que se quedará en el state, por tanto, debe devolver todo lo que había antes (además de la información que cambia)
            palabra: action.payload
        }
    }

    return state;
};

export const selectActiveWord = state => state.palabraReducer.palabra;

store

El store un objeto que contiene todo el arbol de estados de la aplicación. Un estado es una propiedad del store.

En el store se registran los reducers, que como veremos más adelante serán los que modifiquen los estados.

./src/store/index.jsimport { createStore, combineReducers } from 'redux';
import palabraReducer from './palabra/reducer';

const reducers = combineReducers({
    palabraReducer
});

const store = createStore(
    reducers,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;

Finalmente, para cargar React

./src/index.jsimport React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

import { Provider } from 'react-redux';
import store from './store';

const Application = () => (
    <Provider store={store}>
        <App />
    </Provider>
);


ReactDOM.render(<Application />, document.getElementById('root'));

Ejercicios Redux

  1. Modificaremos los ejercicios que vimos en la sección de recogida de datos:

    • Calcular grados Fahrenheit
    • Convertir euros en dólares
    • Calcular la superficie de un rectángulo.

    Cada uno de estos ejercicios debe tener dos componentes. Uno en el que se introducirán los datos y otro en el que se pintarán los resultados. Utilizaremos redux para compartir del valor a través de los diferentes componentes.

    const App = _ => (
        <div className="App">
            <Form/>
            <PintarResultado/>
        </div>
    );

Ejercicio listado con Redux

Crear un componente que consulte un array con un listado de valores y los muestre por pantalla.

Al pulsar sobre un elemento del listado, debemos almacenar su id con Redux y utilizarla para pintar el título de ese elemento recuperándolo con Redux desde otro componente.

El componente del listado, quedaría de la siguiente forma:

import React from "react";
import {connect} from "react-redux";
import { setActiveElement } from '../../store/elementoActivo/actions';

const Listado = ({ setActiveElement }) => {
    const elementos = [
        {id: 1, name: 'elemento1'},
        {id: 2, name: 'elemento2'},
        {id: 3, name: 'elemento3'},
        {id: 4, name: 'elemento4'},
        {id: 5, name: 'elemento5'},
        {id: 6, name: 'elemento6'}
    ];

    return (
        <ul>
            { elementos.map(({ id, name }) => (
                <li
                    onClick={() => setActiveElement(id)}
                    key={id}>{name}
                </li>
            ))}
        </ul>
    );
};

export default connect(null, { setActiveElement })(Listado)

Ejercicio listado de componentes con Redux

El componente (Persona) debe recibir la prop del evento que se ha producido.

Listado{ elementos.map(({ id, name }) => (
  <Persona
    onClick={() => setActiveElement(id)}
    key={id}
    nombre={name} />
  ))}
Personaconst Persona = ({ nombre, onClick }) => {
    return (
        <div onClick={onClick}>{nombre}</div>
    );
};
icono de mandar un mail¡Contacta conmigo!
Pablo Monteserín
contacta conmigoPablo Monteserín

Para dudas técnicas sobre los ejercicios de mis cursos es necesario estar suscrito.