Reaja métodos de ciclo de vida aos ganchos
Tenho um blog simples com artigos. E eu quero reescrever classes para componentes funcionais e ganchos. Agora eu tenho essa lógica nos métodos de ciclo de vida da minha página com o formulário de edição / adição: funciona bem.
componentDidUpdate(prevProps, prevState) {
if (this.props.match.params.id !== prevProps.match.params.id) {
if (prevProps.match.params.id) {
this.props.onUnload();
}
this.id = this.props.match.params.id;
if (this.id) {
return this.props.onLoad(userService.articles.get(this.id));
}
this.props.onLoad(null);
}
this.isLoading = false;
}
componentDidMount() {
if (this.id) {
this.isLoading = true;
return this.props.onLoad(userService.articles.get(this.id));
}
this.isLoading = false;
this.props.onLoad(null);
}
componentWillUnmount() {
this.props.onUnload();
}
shouldComponentUpdate(newProps, newState) {
if (this.props.match.params.id !== newProps.match.params.id) {
this.isLoading = true;
}
return true;
}
Reescrevi tudo para ganchos como esse:
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
}, []);
useEffect(()=> {
prevId.current = id;
}, [id]
);
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
});
//componentWillUnmount
useEffect(() => {
return props.onUnload();
}, []);
- Recebi o erro - "Muitas novas renderizações." em codesandbox código completo: codesandbox
É estranho, mas em localhost não há erro "Muitos re-renderizações."
Não sei o que fazer com meu método de classe "shouldComponentUpdate" como reescrevê-lo para ganchos. Tentei 'memo', mas não tenho ideia de como escrever neste caso.
E, de qualquer forma, estou perdendo algo, porque nem tudo funcionará - ele não está atualizando os campos do formulário corretamente.
Se você tem um bom conhecimento com ganchos de reação, por favor ajude ou dê algum conselho - como consertar?
Respostas
O efeito sem dependência está causando "Too many re-renders."
: ele é executado após cada renderização, em seguida, ele chama setIsLoading
para atualizar o estado ( loading
) que fará com que o componente seja renderizado novamente, o que será executado effect
novamente e o setState
será chamado novamente effect
e assim por diante ...
//componentDidUpdate
useEffect(() => {
//const id = props.match.params.id;
if (id !== prevId.current) {
if (prevId.current) {
props.onUnload();
}
if (id) {
return props.onLoad(userService.articles.get(id));
}
props.onLoad(null);
}
setIsloading(false);
})
para corrigir o problema, remova o setIsLoading
dele ou adicione-o IsLoading
como dependência.
//componentDidUpdate
useEffect(() => {
...
setIsloading(false);
},[isLoading]);
você também pode mesclar dois efeitos de montagem em um como este (o seu também está funcionando, mas acho que é estilisticamente preferível):
//componentDidMount
useEffect(() => {
if (id) {
setIsloading(true);
return props.onLoad(userService.articles.get(id));
}
setIsloading(false);
props.onLoad(null);
//componentWillUnmount
return function () {
props.onUnload()
}
}, []);
para o segundo item: sobre como reescrever o do seu componente shouldComponentUpdate
; primeiro devo apontar que sua existência shouldComponentUpdate
não parece razoável, uma vez que você sempre retorna true
, e você só está usando isso para desencadear o loading
estado; e não é elegível para ser escrito com React.memo (que é equivalente a shouldComponentUpdate
componentes in class); então você só precisa de algo a ser executado em cada mudança de props para determinar o loading
estado e para isso você pode usar um efeito com props
como sua dependência como este:
//manually keeping old props id
const prevPropsId = useRef();
useEffect(() => {
if (prevPropsId.current !== props.match.params.id) {
setIsLoading(true);
}
prevPropsId.current = props.match.params.id;
}, [props.match.params.id]);
// this hook only run if `props.match.params.id` change
com base na minha percepção, isso fará o trabalho, (apesar de ter dificuldade em entender por que você escreve a lógica dessa forma em primeiro lugar) e se não estiver indo bem, você pode ajustar a lógica um pouco para atender às suas necessidades, você obtém o ideia de como o efeito de mudança de adereços funciona. você também precisa lidar typeError
com o caso id
, params
ou match
não existe, e o encadeamento opcional pode ser uma ferramenta útil aqui ->props.match?.params?.id
Se você perder os ganchos do antigo ciclo de vida, poderá sempre recriá-los como ganchos personalizados:
function useComponentDidMount(effect) {
useEffect(() => {
effect();
}, []);
}
function useComponentWillUnmount(effect) {
useEffect(() => {
return effect;
}, []);
}
function useComponentDidUpdate(effect, dependencies) {
const hasMountedRef = useRef(false);
const prevDependenciesRef = useRef(null)
useEffect(() => {
// don't run on first render, only on subsequent changes
if (!hasMountedRef.current) {
hasMountedRef.current = true;
prevDependenciesRef.current = dependencies;
return;
}
// run effect with previous dependencies, like in componentDidUpdate!
effect(...prevDependenciesRef.current || []);
prevDependenciesRef.current = dependencies;
}, dependencies)
}
Uso (seu exemplo refatorado):
//componentDidMount
useComponentDidMount(() => {
if (id) {
setIsloading(true);
props.onLoad(userService.articles.get(id));
return;
}
setIsloading(false);
props.onLoad(null);
});
//componentDidUpdate
useComponentDidUpdate((prevId) => {
if (prevId) {
props.onUnload();
}
if (id) {
props.onLoad(userService.articles.get(id));
return;
}
props.onLoad(null);
setIsloading(false);
}, [id]);
//componentWillUnmount
useComponentWillUnmount(() => {
props.onUnload();
});