Básicamente, necesito renderizar un componente basado en un estado que se establece de forma asíncrona, por defecto el estado es "falso", por lo que el componente se monta y arroja el retorno que corresponde a la opción falsa pero no espera la premisa que actualiza el estado.
export const LayoutValidator=({children})=>{ const [auth,setAuth] = useState(undefined) const {token} = JSON.parse(localStorage.getItem("loggedUser")||'{}'); fetch(`${urlValue}/api/validate-token`,{ method:"POST", headers:{ authorization: token, } }) .then(ans=>ans.json()) .then(ans=>setAuth(ans.auth)) .catch(err=>console.log(err)) return auth ? children : <Navigate to="/" /> }
¿Cómo puedo configurar este componente para que espere la premisa antes de devolver su respuesta?
Puede crear su "gancho de búsqueda" personalizado y allí puede obtener los datos y establecer el estado de carga:
const { useEffect, useState } = React const useFetchUsers = () => { const [users, setUsers] = useState([]) const [loading, setLoading] = useState(false) useEffect(() => { setLoading(() => true) // setTimeout added to emphasize async nature setTimeout(() => { fetch('https://jsonplaceholder.typicode.com/users/') .then(response => response.json()) .then(json => setUsers(() => json)) .finally(setLoading(() => false)) }, 1000) }, []) return { users, loading, } } const UserItem = ({ id, name, username, email }) => { return ( <div> {name} - {username} - {email} </div> ) } const App = () => { const { users, loading } = useFetchUsers() return ( <div> { !loading ? users.map((user) => { return ( <UserItem key={user.id} {...user} /> ) }) : "Loading users..." } </div> ) } ReactDOM.render( <App />, document.getElementById('root') );
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> <div id="root"></div>
Ok, esto no fue tan trivial como pensé (al menos para mí), muchas gracias a @Bergi y a @muka.gergely y a los autores de este recurso que me ayudaron a obtener la solución final: https://www.debuggr. io/react-update-unmounted-component/
Esto es lo que vine con:
export const LayoutValidator=({children})=>{ const {invalid, auth} = useFetchToken() return !invalid ? <Intermediate children={children}/> : <Navigate to="/" /> } function Intermediate ({children}) { const {invalid, auth} = useFetchToken() return !auth ? <SplashScreen/> : children } function useFetchToken() { const { token } = JSON.parse(localStorage.getItem("loggedUser") || '{}') const [invalid, setInvalid] = useState(false) const [auth, setAuth] = useState(false) useEffect(()=>{ let mounted = true setInvalid(() => true) fetch(`${urlValue}/api/validate-token`, { method: "POST", headers: { authorization: token, } }) .then(ans => ans.json()) .then(ans => mounted && setAuth(()=>ans.auth)) .catch(err => console.log(err)) .finally(setInvalid(()=>false)) return () => mounted = false; },[]) return {invalid, auth} }
En mi situación, funcionó perfectamente, espero que esto ayude a alguien en el futuro.