Sin especificar a continuación: gestión de estado minimalista de una aplicación React

200 bytes para la gestión de estado de componentes React

  • Ganchos de reacción : eso es todo lo que se necesita para administrar el estado.
  • ~ 200 bytes , min + gz.
  • API familiar : solo use React como de costumbre.
  • API mínima : cinco minutos son suficientes para resolverlo.
  • Escrito en TypeScript para proporcionar inferencia de tipos automática.

La pregunta principal es: ¿es este paquete mejor que Redux? Bueno ...


  • El es menos. Es 40 veces más pequeño.
  • El es más rápido. Aislar los problemas de rendimiento a nivel de componente.
  • Es más fácil de aprender. En cualquier caso, debe poder usar los ganchos de React y el contexto, son geniales.
  • Es más fácil de integrar. Conecte un componente a la vez sin romper la compatibilidad con otras bibliotecas React.
  • Es más fácil de probar. Probar los reductores por separado es una pérdida de tiempo; simplifique la prueba de los componentes React.
  • Es más simple en términos de mecanografía. Está escrito para maximizar el uso de la inferencia de tipos.
  • El es minimalista. Esto es solo Reaccionar.

Ejemplo de código


import React, { useState } from "react" import { createContainer } from "unstated-next" import { render } from "react-dom" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <span>{counter.count}</span> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } render(<App />, document.getElementById("root")) 

Actitud hacia lo no declarado


Yo (Jamie Kyle - aprox. Por.) Considere esta biblioteca como la sucesora de Unstated . Hice Unstated porque estaba convencido de que React en sí mismo hizo un gran trabajo al administrar el estado, y carecía solo de un mecanismo simple para separar el estado general y la lógica. Por lo tanto, creé Unstated como la solución "mínima" para este problema.


Con la llegada de los ganchos, React se ha vuelto mucho mejor en términos de resaltar el estado general y la lógica. Mucho mejor que, desde mi punto de vista, Unstated se ha convertido en una abstracción innecesaria.


NO MENOS , creo que muchos desarrolladores tienen poca idea de cómo separar la lógica y el estado general de la aplicación usando React Hooks. Esto puede deberse simplemente a la calidad insuficiente de la documentación y la inercia de la comunidad, pero creo que una API clara puede corregir esta deficiencia.


Sin especificar A continuación se encuentra esta misma API. En lugar de ser la "API mínima para compartir el estado y la lógica en React", ahora tiene la "API mínima para comprender cómo compartir el estado y la lógica en React".


Realmente me gusta React, quiero que React prospere. Preferiría que la comunidad abandone el uso de bibliotecas externas para administrar estados como Redux, y finalmente comience a usar las herramientas integradas en React con toda su fuerza.


Si, en lugar de usar Unstated, solo usas React, agradeceré esto. ¡Escríbelo en tus blogs! ¡Habla sobre esto en las conferencias! Comparte tus conocimientos con la comunidad.


Guía sin fecha siguiente


Si aún no está familiarizado con React Hooks, le recomiendo que deje de leer y leer
excelente documentación en el sitio web de React .


Entonces, con la ayuda de ganchos puedes escribir algo como este componente:


 function CounterDisplay() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return ( <div> <button onClick={decrement}>-</button> <p>You clicked {count} times</p> <button onClick={increment}>+</button> </div> ) } 

Si la lógica del componente necesita ser utilizada en varios lugares, se puede eliminar
en un gancho personalizado separado:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } function CounterDisplay() { let counter = useCounter() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

¿Pero qué hacer cuando necesita una condición general, y no solo lógica?
El contexto es útil aquí:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContext(null) function CounterDisplay() { let counter = useContext(Counter) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { let counter = useCounter() return ( <Counter.Provider value={counter}> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Es maravilloso y maravilloso; Cuanta más gente escriba en este estilo, mejor.


Sin embargo, vale la pena agregar un poco más de estructura y claridad para que la API exprese claramente sus intenciones.


Para hacer esto, agregamos la función createContainer() , para que pueda tratar sus ganchos personalizados como "contenedores", de modo que nuestra API nítida y clara sea simplemente imposible de usar incorrectamente.


 import { createContainer } from "unstated-next" function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } function App() { return ( <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Compare el texto del componente antes y después de nuestros cambios:


 - import { createContext, useContext } from "react" + import { createContainer } from "unstated-next" function useCounter() { ... } - let Counter = createContext(null) + let Counter = createContainer(useCounter) function CounterDisplay() { - let counter = useContext(Counter) + let counter = Counter.useContainer() return ( <div> ... </div> ) } function App() { - let counter = useCounter() return ( - <Counter.Provider value={counter}> + <Counter.Provider> <CounterDisplay /> <CounterDisplay /> </Counter.Provider> ) } 

Si escribe en TypeScript (y si no, le recomiendo que se familiarice con él), también obtendrá una mejor inferencia de tipos. Si su gancho personalizado está fuertemente tipado, la salida de todos los demás tipos funcionará automáticamente.


API


createContainer(useHook)


 import { createContainer } from "unstated-next" function useCustomHook() { let [value, setValue] = useState() let onChange = e => setValue(e.currentTarget.value) return { value, onChange } } let Container = createContainer(useCustomHook) // Container === { Provider, useContainer } 

<Container.Provider>


 function ParentComponent() { return ( <Container.Provider> <ChildComponent /> </Container.Provider> ) } 

Container.useContainer()


 function ChildComponent() { let input = Container.useContainer() return <input value={input.value} onChange={input.onChange} /> } 

useContainer(Container)


 import { useContainer } from "unstated-next" function ChildComponent() { let input = useContainer(Container) return <input value={input.value} onChange={input.onChange} /> } 

Consejos


Consejo # 1: Fusionando Contenedores


Como estamos trabajando con ganchos personalizados, podemos combinar contenedores dentro de otros ganchos.


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment, setCount } } let Counter = createContainer(useCounter) function useResettableCounter() { let counter = Counter.useContainer() let reset = () => counter.setCount(0) return { ...counter, reset } } 

Consejo # 2: use envases pequeños


Los contenedores se hacen pequeños y se centran claramente en una tarea específica. Si necesita lógica comercial adicional en contenedores, realice nuevas operaciones en ganchos separados y deje que el estado se almacene en contenedores.


 function useCount() { return useState(0) } let Count = createContainer(useCount) function useCounter() { let [count, setCount] = Count.useContainer() let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) let reset = () => setCount(0) return { count, decrement, increment, reset } } 

Consejo # 3: Optimización de componentes


No existe una "optimización" separada para unstated-next ; los métodos habituales para optimizar los componentes React son suficientes.


1) Optimización de subárboles pesados ​​mediante la división de componentes.


Para:


 function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <div> <div> <div> <div>   </div> </div> </div> </div> </div> ) } 

Después:


 function ExpensiveComponent() { return ( <div> <div> <div> <div>   </div> </div> </div> </div> ) } function CounterDisplay() { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> <ExpensiveComponent /> </div> ) } 

2) Optimización de operaciones pesadas con el gancho useMemo ()


Para:


 function CounterDisplay(props) { let counter = Counter.useContainer() //    ,   `counter` —   let expensiveValue = expensiveComputation(props.input) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

Después:


 function CounterDisplay(props) { let counter = Counter.useContainer() //    ,     let expensiveValue = useMemo(() => { return expensiveComputation(props.input) }, [props.input]) return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

3) Reduzca el número de renders usando React.memo () y useCallback ()


Para:


 function useCounter() { let [count, setCount] = useState(0) let decrement = () => setCount(count - 1) let increment = () => setCount(count + 1) return { count, decrement, increment } } let Counter = createContainer(useCounter) function CounterDisplay(props) { let counter = Counter.useContainer() return ( <div> <button onClick={counter.decrement}>-</button> <p>You clicked {counter.count} times</p> <button onClick={counter.increment}>+</button> </div> ) } 

Después:


 function useCounter() { let [count, setCount] = useState(0) let decrement = useCallback(() => setCount(count - 1), [count]) let increment = useCallback(() => setCount(count + 1), [count]) return { count, decrement, increment } } let Counter = createContainer(useCounter) let CounterDisplayInner = React.memo(props => { return ( <div> <button onClick={props.decrement}>-</button> <p>You clicked {props.count} times</p> <button onClick={props.increment}>+</button> </div> ) }) function CounterDisplay(props) { let counter = Counter.useContainer() return <CounterDisplayInner {...counter} /> } 

Migración con unstated


Publico esta biblioteca a propósito como un paquete separado porque toda la API es completamente nueva. Por lo tanto, puede instalar ambos paquetes en paralelo y migrar gradualmente.


Comparta sus impresiones sobre la transición a unstated-next , porque en los próximos meses planeo hacer dos cosas con base en esta información:


  • Asegúrese de que unstated-next satisfaga todas las necesidades de los usuarios unstated .
  • Asegúrese de que, para los unstated haya un proceso claro y conciso de migración a unstated-next .

Quizás agregaré algunas API a la biblioteca nueva o antigua para facilitar la vida de los desarrolladores. En cuanto unstated-next , prometo que las API agregadas serán lo más mínimas posibles, y haré todo lo posible para mantener la biblioteca pequeña.


En el futuro, probablemente unstated-next código sin unstated nuevamente como una nueva versión principal. unstated-next todavía estará disponible para que pueda usar unstated@2 y unstated-next en el mismo proyecto en paralelo. Luego, cuando termine la migración, puede actualizar a unstated@3 y eliminar unstated-next (por supuesto, actualizar todas las importaciones ... debería haber suficiente búsqueda y reemplazo).


A pesar del cambio dramático en la API, espero poder brindarle la migración más fácil posible. Me agradaría cualquier comentario sobre lo que podría hacerse mejor.


Referencias


Source: https://habr.com/ru/post/451410/


All Articles