Efeito
A API effect complementa a API state fornecendo uma maneira melhor de reagir a múltiplas mudanças de estado com base no que você precisa executar.
type EffectSubscriber<T> = (value: T | undefined) => undefined | T
type EffectUnSubscriber = () => void
effect: <T>(sub: EffectSubscriber<T>) => EffectUnSubscriberA API state permite que você assine alterações de seu valor.
const [count, updateCount] = state(0, () => {
// react to changes
})
updateCount(10)Isso é ótimo se você deseja causar efeitos colaterais relacionados a um único estado. Para realizar efeitos colaterais em vários estados, você precisará da API effect.
const [count, updateCount] = state(0)
effect(() => {
// react to changes
})
updateCount(10)EffectUnSubscriber
Alguns efeitos colaterais podem permanecer continuamente como um efeito global para algo específico. Outros precisam ser limpos sempre que o escopo onde estão definidos desaparece.
const cleanEffect = effect(() => {
// react to changes
})
cleanEffect()Como funciona?
Quando você chama um StateGetter dentro da função de retorno de chamada effect, o effect toma conhecimento do estado e essa detecção é feita de forma síncrona.
O efeito chama o retorno de chamada fornecido assim que é declarado, para que ele tome conhecimento dos estados chamados dentro dele.
effect(() => {
console.log(count()) // will log right away
})O effect também atualiza em lote, o que permite atualizar vários estados de uma vez e ter apenas uma única reação.
effect(() => {
console.log(count(), total())
// prints: 0 0 on initiation
// prints: 1 1 on update of both values
})
setCount((prev) => prev + 1)
setTotal((prev) => prev + 1)A atualização em lote é útil, mas o effect também entende a inicialização, o que permite fazer vários cálculos durante a carga e executá-los apenas uma vez após terminar.
effect(() => {
console.log(count())
// prints: 0 on initiation
// print: 100 after the while loop completes
})
while (count() < 100) {
setCount((prev) => prev + 1)
}Cache
O effect permite retornar valores que são armazenados em cache e fornecidos no retorno de chamada para a próxima vez. Isso é ótimo para rastrear alterações nas atualizações.
Por exemplo, execute um efeito de rejeição nas alterações do valor de pesquisa.
const [search, setSearch] = state('')
const [searchResults, updateSearchResults] = state([])
effect((timer) => {
clearTimeout(timer)
// so the effect becomes aware of "search" state
const searchValue = search()
return setTimeout(async () => {
const response = api.search({ searchValue })
updateSearchResults(response.results)
}, 300)
})Efeito assíncrono
O effect funciona de forma síncrona. É assim que ele detecta os estados internos e armazena os dados em cache.
No entanto, o retorno de chamada fornecido ao effect pode ser assíncrono se você realmente desejar.
effect(async () => {
try {
const res = await fetch(
`https://randomuser.me/api/?page=${count()}&results=10&seed=markup`
)
console.log(await res.json())
} catch (e) {
console.error(e)
}
})Se você fizer isso, os dados armazenados em cache serão a promessa retornada pela função e não os dados retornados do retorno de chamada assíncrono. Isso significa que você precisa resolver os dados armazenados em cache para obter o valor.
effect(async (res = Promise.resolve(0)) => {
const result = count() + (await res)
console.log(result)
return result
})Efeito aninhado
Você pode aninhar effect para rastrear valores diferentes. Isso permite que o corpo do seu effect reaja de forma independente enquanto diferentes efeitos rastreiam diferentes estados.
const unsub = effect(() => {
console.log('outer', count())
effect(() => {
console.log('inner', count())
})
})
unsub() // clears all effectsSe quiser limpar todos os efeitos, você pode cancelar a assinatura do effect mais externo e que cuidará de todos os filhos effect para você. No entanto, os efeitos internos devem ser rastreados por você, pois sempre que o efeito externo for chamado, um novo efeito interno será criado.