200 bytes para gerenciamento de estado dos componentes do React
- Ganchos de reação : basta para gerenciar o estado.
- ~ 200 bytes , min + gz.
- API familiar : basta usar o React como de costume.
- API mínima : cinco minutos são suficientes para descobrir.
- Escrito em TypeScript para fornecer inferência automática de tipo.
A principal questão é: este pacote é melhor que o Redux? Bem ...
- Ele é menos É 40 vezes menor.
- Ele é mais rápido. Isolar problemas de desempenho no nível do componente.
- É mais fácil aprender. De qualquer forma, você precisa poder usar os ganchos e o contexto do React, pois são legais.
- É mais fácil de integrar. Conecte um componente de cada vez sem quebrar a compatibilidade com outras bibliotecas do React.
- É mais fácil testar. Testar redutores separadamente é uma perda de tempo; simplifique o teste dos componentes do React.
- É mais simples em termos de digitação. Está escrito para maximizar o uso da inferência de tipo.
- Ele é minimalista. Isso é apenas reagir.
Exemplo 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"))
Atitude em relação ao não declarado
I (Jamie Kyle - aprox. Por.) Considere esta biblioteca como sucessora de Não declarado . Fiz Unstate porque estava convencido de que o próprio React fazia um ótimo trabalho de gerenciamento do estado, e faltava apenas um mecanismo simples para separar o estado geral e a lógica. Portanto, criei Unstated como a solução "mínima" para esse problema.
Com o advento dos ganchos, o React se tornou muito melhor em termos de destaque do estado geral e da lógica. Tanto melhor que, do meu ponto de vista, o Unstated se tornou uma abstração desnecessária.
NÃO MENOS , acredito que muitos desenvolvedores têm pouca idéia de como separar a lógica e o estado geral do aplicativo usando ganchos React. Isso pode ser simplesmente devido à qualidade insuficiente da documentação e à inércia da comunidade, mas acredito que uma API clara possa corrigir essa falha.
Não declarado A seguir é essa mesma API. Em vez de ser a "API mínima para compartilhar estado e lógica no React", agora ela tem a "API mínima para entender como compartilhar estado e lógica no React".
Eu realmente gosto de React, quero que React floresça. Prefiro que a comunidade abandone o uso de bibliotecas externas para gerenciar estados como o Redux e, finalmente, comece a usar as ferramentas incorporadas ao React com força total.
Se, em vez de usar Unstated, você apenas usar React - eu aceitarei isso. Escreva sobre isso em seus blogs! Fale sobre isso em conferências! Compartilhe seu conhecimento com a comunidade.
Unstated-next Guide
Se você ainda não está familiarizado com os ganchos do React, recomendo que pare de ler e ler
excelente documentação no site do React .
Portanto, com a ajuda de hooks, você pode escrever 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> ) }
Se a lógica de componentes precisar ser usada em vários locais, ela poderá ser retirada
em um 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> ) }
Mas o que fazer quando você precisa de uma condição geral, e não apenas lógica?
O contexto é útil aqui:
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> ) }
É maravilhoso e maravilhoso; quanto mais as pessoas escrevem nesse estilo, melhor.
No entanto, vale a pena adicionar um pouco mais de estrutura e clareza para que a API expresse suas intenções com muita clareza.
Para fazer isso, adicionamos a função createContainer()
, para que você possa tratar seus ganchos personalizados como "contêineres", para que nossa API clara e nítida seja simplesmente impossível de usar incorretamente.
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 o texto do componente antes e depois das nossas alterações:
- 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> ) }
Se você escrever no TypeScript (e se não, eu recomendo fortemente que você se familiarize com ele), também obterá uma melhor inferência de tipo. Se seu gancho personalizado for fortemente digitado, a saída de todos os outros tipos funcionará automaticamente.
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>
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} /> }
Dicas
Dica 1: mesclando contêineres
Como lidamos com ganchos personalizados, podemos combinar contêineres dentro de outros 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 } }
Dica 2: use recipientes pequenos
Os recipientes devem ser pequenos e focados claramente em uma tarefa específica. Se você precisar de lógica comercial adicional em contêineres - realize novas operações em ganchos separados e deixe o estado ser armazenado em contêineres.
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 } }
Dica 3: Otimização de componentes
Não existe uma “otimização” separada para o unstated-next
; os métodos usuais para otimizar os componentes do React são suficientes.
1) Otimização de subárvores pesadas dividindo 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> ) }
Depois:
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) Otimização de operações pesadas com o gancho useMemo ()
Para:
function CounterDisplay(props) { let counter = Counter.useContainer()
Depois:
function CounterDisplay(props) { let counter = Counter.useContainer()
3) Reduza o número de novas renderizações usando React.memo () e 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> ) }
Depois:
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} /> }
Migração com unstated
Publico propositadamente esta biblioteca como um pacote separado, porque toda a API é completamente nova. Portanto, você pode instalar os dois pacotes em paralelo e migrar gradualmente.
Compartilhe suas impressões da transição para unstated-next
, porque nos próximos meses planejo fazer duas coisas com base nessas informações:
- Certifique-se de que
unstated-next
atenda a todas as necessidades de usuários unstated
. - Certifique-se de que para
unstated
exista um processo claro e conciso de migração para unstated-next
.
Talvez eu adicione algumas APIs à biblioteca antiga ou nova para facilitar a vida dos desenvolvedores. Quanto ao unstated-next
, prometo que as APIs adicionadas serão o mínimo possível e farei o possível para manter a biblioteca pequena.
No futuro, provavelmente unstated-next
o código unstated-next
volta para unstated
como uma nova versão principal. unstated-next
ainda estará disponível para que você possa usar unstated@2
e unstated-next
no mesmo projeto em paralelo. Em seguida, ao concluir a migração, você pode atualizar para unstated@3
e excluir unstated-next
(é claro, atualizando todas as importações ... deve haver pesquisa e substituição suficientes).
Apesar da mudança drástica na API, espero poder fornecer a migração mais fácil possível. Eu ficaria feliz em quaisquer comentários sobre o que poderia ser feito melhor.
Referências