Introducción
Dime cómo manejas el estado y te diré...
Estado y ciclo de vida con React.Component
Nuevo proyecto: códigos de seguridad
Estados simples: React.Component vs. useState
Efectos con useEffect
Métodos del ciclo de vida en React.Component
Estados independientes y compuestos
Estados independientes con useState
¿Dónde actualizar el estado?
Estados compuestos con React.Component
Estados compuestos con useState
Código imperativo y declarativo en React
Estados imperativos con useState
Estados semideclarativos con useState
¿Qué es un reducer?
3 formas de crear un reducer
Estados declarativos con useReducer
Action creators y actionTypes
Manejo del estado en TODO Machine
¿Qué son los estados derivados?
Migración de useState a useReducer en useLocalStorage
Imponer tu visión vs. trabajar en equipo
Próximos pasos
¿Quieres más cursos de React.js?
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Aportes 36
Preguntas 2
Jah, el profe no me gano, puse pausa al video y termine primero !
Action Types: consiste en usar un objeto para almacenar las keys de nuestro reducer:
const actionTypes = {
error: 'ERROR',
confirm: 'CONFIRM',
}
Action Creators: consiste en declarar funciones que contienen nuestros dispatch:
const onError = () => dispatch({ type: actionTypes.error });
const onWrite = ({ target: { value } }) => {
dispatch({ type: actionTypes.write, payload: value });
}
Min 11:26
No es que el lenguaje le ponga los parentesis, sino que le estamos dando una referencia de esa variable, cuando JS va a la variable se da cuenta que es una funcion, y la ejecuta. ✨
Mi mamá ortografica estará fudiosa 🤣
Le agregue la propiedad de disabled al input ya que me parecía algo raro que podamos seguir escribiendo cuando el loading esta en true.
<input
disabled={state.loading}
placeholder="Codigo de seguridad"
value={state.value}
onChange={onWrite}
/>
Así cuando loading sea true, disabled será true y viceversa.
actionTypes
Action creators:
Ese momento donde agradeces tener GitHub Copilot y le ganas al profe xd, tal vez hice un poco de trampa jajaja
Agregue algunos estilos básicos…
.
Me volvió a pasar lo mismo que con los otros cursos de este profesor, aunque considero que explica muy bien y sabe mucho;
te marea demasiado cuando va borrando y cambiando tanto el código. Tal vez está bueno que se explique de una como sería, o buscar otro método de explicar los conceptos. Realmente es bastante confusa esta manera de explicar.
Usando actionTypes con switch/case:
import React, { useEffect, useReducer, Fragment } from 'react'
const SECURITY_CODE = 'paradigma'
const initialState = {
value: '',
loading: false,
error: false,
deleted: false,
confirmed: false,
}
const actionTypes = {
error: 'Error',
confirm: 'Confirm',
write: 'Write',
check: 'Check',
delete: 'Delete',
reset: 'Reset',
}
const reducer = (state, action) => {
switch (action.type) {
case actionTypes.error :
return {
...state,
error: true,
loading: false,
}
case actionTypes.confirm :
return {
...state,
loading: false,
error: false,
confirmed: true,
}
case actionTypes.write :
return {
...state,
value: action.payload,
}
case actionTypes.check :
return {
...state,
loading: true,
error: false,
}
case actionTypes.delete :
return {
...state,
deleted: true,
}
case actionTypes.reset :
return {
...state,
value: '',
confirmed: false,
deleted: false,
}
default:
return {
...state,
}
}
}
export default function UseReducer() {
const [ state, dispatch ] = useReducer(reducer, initialState)
useEffect(
() => {
if (state.loading) {
setTimeout(() => {
if (state.value === SECURITY_CODE) {
dispatch({ type: actionTypes.confirm })
} else {
dispatch({ type: actionTypes.error })
}
}, 1000)
}
},
[ state.loading ]
)
if (!state.deleted && !state.confirmed) {
return (
<div>
<h2>Eliminar UseReducer</h2>
<p>Por favor, escriba el código de seguridad.</p>
{state.loading ? 'Cargando...' : state.error ? 'Error :(' : null}
<br />
<input
type='text'
placeholder='código de seguridad'
value={state.value}
onChange={ev => dispatch({ type: actionTypes.write, payload: ev.target.value })}
/>
<button onClick={() => dispatch({ type: actionTypes.check })}>Comprobar</button>
</div>
)
} else if (!state.deleted && state.confirmed) {
return (
<Fragment>
<p>Pedimos confirmación. ¿Tas seguro?</p>
<button onClick={() => dispatch({ type: actionTypes.delete })}>Si, eliminar</button>
<button onClick={() => dispatch({ type: actionTypes.reset })}>No, me arrepentí</button>
</Fragment>
)
} else {
return (
<Fragment>
<p>Eliminado con éxito</p>
<button onClick={() => dispatch({ type: actionTypes.reset })}>Regresar</button>
</Fragment>
)
}
}
Creería que la parte de Action Types no sería necesaria si utilizamos Typescript para React. Ya que explícitamente puedo declararle los tipos que puede soportar la key “type” de la siguiente forma:
type Action ={
type: ‘WRITE’ | ‘CONFIRM’ | ‘ERROR’ | ‘DELETE’ | ‘CHECK’ | ‘RESET’,
payload?: string,
}
Entonces si me llego a equivocar, TS automáticamente lo detecta y me arroja el error.
Yo pase el argumento de onWrite de la siguiente manera :
<input
placeholder="Código de Seguridad"
value={state.value}
onChange={e => {
onWrite(e.target.value)
}}/>
y en el action creator
const onWrite = (newValue) => {
setState({
...state,
value: newValue
})
}
Este es el resultado final de el proyecto trabajado en este curso
No se hasta que punto conviene hacer el codigo tan declarativo… lo cuestiono mucho
por que se complica tanto ??? me parece innesesario e.e
con TypeScript podemos evitar los errores ortograficos, utilizando type o interfaces
type State = {
error: boolean,
loading: boolean,
value: string,
deleted: boolean,
confirmed: boolean
}
type Action = {
type: "CHECK" | "ERROR" | "CONFIRM" | "WRITE" | "DELETED" | "RESET",
payload?: string
}
const reducerObject = <T,>(state: T, payload = "") => ({
CONFIRM: {
...state,
error: false,
loading: false,
confirmed: true
},
ERROR: {
...state,
error: true,
loading: false
},
CHECK: {
...state,
loading: true
},
WRITE: {
...state,
value: payload
},
DELETED: {
...state,
deleted: true
},
RESET: {
...state,
confirmed: false,
deleted: false,
value: ""
}
})
const reducer = (state: State, action: Action): State => {
return reducerObject(state, action.payload)[action.type]
}
Sintaxis para asignar el valor de una variable como llave de un objeto:
const username = "juan"
const obj = {
[username]: "hernandez"
}
console.log(obj.juan) //hernandez
Lo que mas me gusto de esta clase fue la forma en la que utilizamos el actionTypes que si uno quisiera con typescript podria usarlo como un enum y listo ya no mas switches
Super útil, gracias!
Me recuerda a los enums en Kotlin
Si bien en el onChange se resumia en cuanto a la legibilidad aconsejo que se haga de siguiente, en resumen quedarnos con la anterior version, esto ya que cuando se intenta dar soporte a proyectos grandes estos necesitan tal cual ser ‘faciles de leer’
onChange={(event) => onWrite(event.target.value)}
Complexity(lineal), si me equivoco me corrigen por favor:
const dun = (event.target.value) => (value) => {
dispatch({ type: actionTypes.write, payload: value})
};
Fuente: https://www.cs.us.es/~jalonso/cursos/i1m-19/temas/tema-28.html
Usar types en typescript o interface para el tipado es otra alternativa para este caso.
Action Creators
Incluso podemos crear un archivo propio para los actionTypes:
export const ACTION_TYPES = {
CHECK: 'CHECK',
CONFIRM: 'CONFIRM',
DELETE: 'DELETE',
ERROR: 'ERROR',
RESET: 'RESET',
WRITE: 'WRITE',
}
Ya después lo mandamos a llamar en el archivo que lo necesitemos:
import { useReducer, useEffect, useCallback } from 'react'
import { ACTION_TYPES } from './actionTypes'
import './UseState.css'
const SECURITY_CODE = 'techzone'
function UseReducer({name}) {
const [state, dispatch] = useReducer(reducer, initialState)
const onCheck = () => dispatch({ type: ACTION_TYPES.CHECK })
const onConfirm = () => dispatch({ type: ACTION_TYPES.CONFIRM })
const onDelete = () => dispatch({ type: ACTION_TYPES.DELETE })
const onError = () => dispatch({ type: ACTION_TYPES.ERROR })
const onReset = () => dispatch({ type: ACTION_TYPES.RESET })
const onWrite = ({target: { value }}) => dispatch({ type: ACTION_TYPES.WRITE, payload: value })
const changeState = useCallback(() => {
if (state.loading) {
setTimeout(() => {
state.value === SECURITY_CODE ? onConfirm() : onError()
}, 2000)
}
}, [state])
useEffect(() => {
changeState()
}, [changeState])
if (!state.deleted && !state.confirmed) {
return (
<div>
<h2>Eliminar {name}</h2>
<p>Por favor escribe el código de seguridad</p>
{ state.error && (<p>Error: el código es incorrecto</p>) }
{ state.loading && (<p>Loading...</p>) }
<input
type="text"
placeholder="Código de seguridad"
value={state.value}
onChange={onWrite}
/>
<button onClick={onCheck}
>
Comprobar
</button>
</div>
)
} else if (state.confirmed && !state.deleted) {
return (
<>
<p>
Advertencia ⚠️, ¿Estás seguro de querer eliminar esto?
<span className='red'>
Es irrecuperable
</span>
</p>
<button onClick={onDelete}>
Sí, eliminar
</button>
<button onClick={onReset}>
No, me arrepentí
</button>
</>
)
} else {
return (
<>
<p>Eliminado con éxito</p>
<button onClick={onReset}>
Resetear
</button>
</>
)
}
}
const initialState = {
confirmed: false,
deleted: false,
error: false,
loading: false,
value: '',
}
const reducerObject = (state, payload) => ({
[ACTION_TYPES.WRITE]: {
...state,
value: payload
},
[ACTION_TYPES.CONFIRM]: {
...state,
error: false,
confirmed: true,
loading: false
},
[ACTION_TYPES.ERROR]: {
...state,
error: true,
loading: false
},
[ACTION_TYPES.CHECK]: {
...state,
loading: true,
},
[ACTION_TYPES.DELETE]: {
...state,
deleted: true
},
[ACTION_TYPES.RESET]: {
...state,
confirmed: false,
deleted: false,
value: ''
}
})
const reducer = (state, action) => {
if (reducerObject(state)[action.type]) {
return reducerObject(state, action.payload)[action.type]
} else {
return state
}
}
export { UseReducer }
```jsx
importReactfrom "react";
const SECURITY_CODE = "paradigma";
function UseReducer({name}) {
const [state, dispatch] =React.useReducer(reducer, initialState);
const onConfirm = () => {
dispatch({type: actionTypes.confirm});
}
const onError = () => {
dispatch({type: actionTypes.error});
}
const onWrite = (eventValue) => {
dispatch({type: actionTypes.write, payload:eventValue});
}
const onCheck = () => {
dispatch({type: actionTypes.check});
}
const onDelete = () => {
dispatch({type: actionTypes.delete});
}
const onReset = () => {
dispatch({type: actionTypes.reset});
}
React.useEffect(() => {
if (!!state.loading) {
setTimeout(() => {
if (state.value === SECURITY_CODE) {
onConfirm()
} else {
onError()
}
}, 3000);
}
}, [state.loading]);
if (!state.deleted && !state.confirmed) {
return (
<React.Fragment>
<h2>Eliminar{name}</h2>
<p>Por favor, escribe el código de seguridad</p>
{(!state.loading && state.error) && (
<p>El Código es incorrecto</p>
)}
{state.loading && (
<p>Cargando...</p>
)}
<input
value={state.value}
onChange={(event) => {
onWrite(event.target.value)
}}
placeholder="Código de Seguridad"
/>
<button
onClick={onCheck}
>Comprobar
</button>
</React.Fragment>
);
} else if (!!state.confirmed && !state.deleted) {
return (
<React.Fragment>
<h2>Eliminar{name}</h2>
<p>¿Seguro que quieres eliminar{name}?</p>
<button type="button" onClick={onDelete}>Si, eliminar
</button>
<button type="button" onClick={onReset}>No, cancelar
</button>
</React.Fragment>
);
} else {
return (
<React.Fragment>
<h2>¡{name}eliminado!</h2>
<button type="button" onClick={onReset}>Recuperar{name}</button>
</React.Fragment>
);
}
}
const initialState = {
value: "",
error: false,
loading: false,
deleted: false,
confirmed: false
};
const actionTypes = {
confirm: 'CONFIRM',
error: 'ERROR',
write: 'WRITE',
check: 'CHECK',
delete: 'DELETE',
reset: 'RESET',
}
const reducerObject = (state,payload= false) => ({
[actionTypes.confirm]: {
...state,
error: false,
loading: false,
confirmed: true
},
[actionTypes.error]: {
...state,
error: true,
loading: false
},
[actionTypes.write]: {
...state,
value:payload
},
[actionTypes.check]: {
...state,
loading: true
},
[actionTypes.delete]: {
...state,
deleted: true
},
[actionTypes.reset]: {
...state,
confirmed: false,
deleted: false,
value: ''
},
});
const reducer = (state,action) => {
if (reducerObject(state)[action.type]) {
return reducerObject(state,action.payload)[action.type]
} else {
returnstate;
}
}
export {UseReducer};
Un gran poder, conlleva una gran responsabilidad.
ayuda! tenia que haber parado hace 5 clases, esto está muy interesante.
Por favor, escribe el código de seguridad.
{state.error &&Error codigo incorrecto
} {state.loading &&cargando...
} <input type="text" placeholder="escribe el godigo de seguridad" onChange={onWrite} /> <button onClick={onCheck}>Comprobar</button>Seguro que quiere eliminar?
<button onClick={onDelete}>si, eliminar</button> <button onClick={onReset}>no, quiero cancelar</button> ); } else { return ( <>Eliminado con éxito
<button onClick={onReset}>resetear, volver atrás</button> ); } } export { UseReducer }; ```le falto el onCheck en el objeto actionTypes, mi código declarativo con reducers: import { useEffect, useReducer } from "react"; const SECURITY\_CODE = "paradigma"; function UseReducer({ name }) { const initialState = { value: "", error: false, loading: false, deleted: false, confirmed: false, }; const actionTypes = { error: "ERROR", confirm: "CONFIRM", check: "CHECK", write: "WRITE", delete: "DELETE", reset: "RESET", }; const reducer = (state, action) => { switch (action.type) { case actionTypes.error: return { ...state, error: true, loading: false, }; case actionTypes.confirm: return { ...state, loading: false, confirmed: true, }; case actionTypes.check: return { ...state, loading: true, error: false, }; case actionTypes.write: return { ...state, value: action.payload, }; case actionTypes.delete: return { ...state, deleted: true, }; case actionTypes.reset: return { ...state, deleted: false, confirmed: false, }; default: return state; } }; const \[state, dispatch] = useReducer(reducer, initialState); useEffect(() => { console.log("iniciando efecto"); if (state.loading) { console.log("Haciendo validación en efecto"); setTimeout(() => { if (state.value !== SECURITY\_CODE) { onError(); } else { onConfirm(); } }, 3000); } }, \[state.loading]); const onError = () => { dispatch({ type: actionTypes.error }); }; const onConfirm = () => { dispatch({ type: actionTypes.confirm }); }; const onCheck = () => { dispatch({ type: actionTypes.check }); }; const onWrite = (event) => { dispatch({ type: actionTypes.write, payload: event.target.value }); }; const onDelete = () => { dispatch({ type: actionTypes.delete }); }; const onReset = () => { dispatch({ type: actionTypes.reset }); }; if (!state.deleted && !state.confirmed) { return ( \Por favor, escribe el código de seguridad.\
{state.error && \Error codigo incorrecto\
} {state.loading && \cargando... \
} \<input type="text" placeholder="escribe el godigo de seguridad" onChange={onWrite} /> \<button onClick={onCheck}>Comprobar\</button> \Seguro que quiere eliminar?\
\<button onClick={onDelete}>si, eliminar\</button> \<button onClick={onReset}>no, quiero cancelar\</button> \ ); } else { return ( <> \Eliminado con éxito\
\<button onClick={onReset}>resetear, volver atrás\</button> \ ); } } export { UseReducer }; ````Por favor, escribe el código de seguridad.\
{state.error && \Error codigo incorrecto\
} {state.loading && \cargando... \
} \<input type="text" placeholder="escribe el godigo de seguridad" onChange={onWrite} /> \<button onClick={onCheck}>Comprobar\</button> \Seguro que quiere eliminar?\
\<button onClick={onDelete}>si, eliminar\</button> \<button onClick={onReset}>no, quiero cancelar\</button> \ ); } else { return ( <> \Eliminado con éxito\
\<button onClick={onReset}>resetear, volver atrás\</button> \ ); } } export { UseReducer };Por favor, escribe el código de seguridad.\
{state.error && \Error codigo incorrecto\
} {state.loading && \cargando... \
} \<input type="text" placeholder="escribe el godigo de seguridad" onChange={onWrite} /> \<button onClick={onCheck}>Comprobar\</button> \Seguro que quiere eliminar?\
\<button onClick={onDelete}>si, eliminar\</button> \<button onClick={onReset}>no, quiero cancelar\</button> \ ); } else { return ( <> \Eliminado con éxito\
\<button onClick={onReset}>resetear, volver atrás\</button> \ ); } } export { UseReducer };no me ganaste, porque yo te puse en pausa mientras lo hice. Técnicamente detuve tu tiempo. así que a pesar de usar ‘cámara rápida’ yo claramente salí ganador
Código de la clase en TypeScript:
Debo decir que en este caso TS nos ofrece unas interesantes formas de trabajar esto:
import { ChangeEvent, FC, useEffect, useReducer } from "react"
type Props = {
name: string
}
type State = {
value: string,
error: boolean,
loading: boolean,
deleted: boolean,
confirmed: boolean,
}
enum ActionTypes {
CHECK = 'CHECK',
ERROR = 'ERROR',
CONFIRM = 'CONFIRM',
DELETE = 'DELETE',
RESET = 'RESET',
WRITE = 'WRITE',
}
interface Action {
type: ActionTypes,
payload?: any
}
const initialState:State = {
value: '',
error: false,
loading: false,
confirmed: false,
deleted: false,
};
const SECURITY_CODE = 'paradigma';
const UseReducer:FC<Props> = ({ name }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const onConfirmed = () => {dispatch({ type: ActionTypes.CONFIRM })};
const onError = ()=>{dispatch({ type: ActionTypes.ERROR })};
const onWrite = ({ target : { value }}:ChangeEvent<HTMLInputElement>)=>{dispatch({ type: ActionTypes.WRITE , payload: value})};
const onCheck = ()=>{dispatch({ type: ActionTypes.CHECK })};
const onDelete = ()=>{dispatch({ type: ActionTypes.DELETE })};
const onReset = ()=>{dispatch({ type: ActionTypes.RESET })};
console.log(state);
useEffect(()=>{
console.log('Empezando el efecto');
if(state.loading){
console.log(state. loading);
setTimeout(()=>{
console.log("Haciendo la validación");
if(state.value === SECURITY_CODE){
onConfirmed();
} else {
onError();
}
console.log("Terminando la validación");
},3000)
}
console.log('Terminando el efecto');
},[state.loading]);
if(!state.deleted && !state.confirmed){
return(
<div>
<h2>Eliminar { name }</h2>
<p>Por favor, escribe el código de seguridad para comprobar que quieres eliminar</p>
{state.error && !state.loading && (
<p>El código es incorrecto</p>
)}
{state.loading && (
<p>Cargando ...</p>
)}
<input
type="text"
placeholder="Código de seguridad"
value={state.value}
onChange={onWrite}
/>
<button
onClick={()=>{
onCheck();
}}
>Comprobar</button>
</div>
)} else if(!state.deleted && state.confirmed) {
return(
<>
<p>¿Seguro que quieres eliminar { name }</p>
<button
onClick={onDelete}
>Si, eliminar</button>
<button
onClick={onReset}
>No, volver</button>
</>
)
} else {
return(
<>
<p>Estado de eliminación</p>
<button
onClick={onReset}
>Resetear, volver atrás</button>
</>
)
}
}
const reducerObject = (state: State, action:Action): {[k in ActionTypes]: State} => ({
CONFIRM:{
...state,
loading: false,
error: false,
confirmed: true,
},
ERROR: {
...state,
loading: false,
error: true,
},
CHECK: {
...state,
loading: true,
},
DELETE:{
...state,
deleted: true
},
RESET: {
...state,
confirmed: false,
deleted: false,
value: '',
},
WRITE:{
...state,
value: action.payload
}
});
const reducer = (state: State, action: Action) : State =>reducerObject(state, action)[action.type] || state;
export { UseReducer }
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?
o inicia sesión.