He estado resolviendo este problema sin ningún progreso durante las últimas 2 horas, aquí está el código:
export const useFetchAll = () => {
const [searchResult, setSearchResult] = useState([]);
const [loading, setLoading] = useState(false);
const [searchItem, setSearchItem] = useState("");
const [listToDisplay, setListToDisplay] = useState([]);
// const debouncedSearch = useDebounce(searchItem, 300);
const handleChange = (e) => {
setSearchItem(e.target.value);
if (searchItem === "") {
setListToDisplay([]);
} else {
setListToDisplay(
searchResult.filter((item) => {
return item.name.toLowerCase().includes(searchItem.toLowerCase());
})
);
}
console.log(searchItem);
};
useEffect(() => {
const searchRepo = async () => {
setLoading(true);
const { data } = await axios.get("https://api.github.com/repositories");
setSearchResult(data);
setLoading(false);
};
if (searchItem) searchRepo();
}, [searchItem]);
el problema es que cuando ingreso caracteres en la entrada y configuro el estado en event.target.value, no selecciona el último carácter. aquí hay una imagen: ingrese la descripción de la imagen aquí
Por cierto, este es un gancho personalizado, devuelvo la función onchange aquí:
const HomePage = () => {
const { searchResult, loading, searchItem, handleChange, listToDisplay } =
useFetchAll();
y luego pasarlo como accesorio a un componente así:
<Stack spacing={2}>
<Search searchItem={searchItem} handleChange={handleChange} />
</Stack>
</Container>
¿alguna ayuda? gracias de antemano.
Está manejando las searchResult de estado searchItem y searchResult como si su cambio de estado fuera síncrono (a través setSearchItem y setSearchResult ), ¡pero no lo es! Los setters de estado React son asincrónicos .
La devolución de llamada useEffect depende de la variable de estado searchItem . Ahora, cada vez que el usuario escriba algo, el estado cambiará, ese cambio activará una nueva representación del Componente y, una vez finalizada la representación, el side-effect (la devolución de llamada useEffect ) se ejecutará debido al ciclo de vida de los Componentes.
En nuestro caso, no queremos iniciar la solicitud de recuperación en el siguiente procesamiento, sino justo en el momento en que el usuario ingresa algo en el campo de entrada de búsqueda, es cuando se activa handleChange .
Para que el código funcione como se espera, necesitamos una refactorización más estructural.
Puedes deshacerte del useEffect y manejar el flujo a través del método handleChange :
export const useFetchAll = () => {
const [ loading, setLoading ] = useState( false );
const [ searchItem, setSearchItem ] = useState( "" );
const [ listToDisplay, setListToDisplay ] = useState( [] );
const handleChange = async ( e ) => {
const { value } = e.target;
// Return early if the input is an empty string:
setSearchItem( value );
if ( value === "" ) {
return setListToDisplay( [] );
}
setLoading( true );
const { data } = await axios.get( "https://api.github.com/repositories" );
setLoading( false );
const valueLowercase = value.toLowerCase(); // Tiny optimization so that we don't run the toLowerCase operation on each iteration of the filter process below
setListToDisplay(
data.filter(({ name }) => name.toLowerCase().includes(valueLowercase))
);
};
return {
searchItem,
handleChange,
loading,
listToDisplay,
};
};
la función utilizada para actualizar el valor del estado es asíncrona, por eso su variable de estado muestra el valor anterior y no el valor actualizado. He realizado algunos cambios, puede intentar ejecutar el siguiente código.
const [searchResult, setSearchResult] = useState([]);
const [loading, setLoading] = useState(false);
const [searchItem, setSearchItem] = useState("");
const [listToDisplay, setListToDisplay] = useState([]);
// const debouncedSearch = useDebounce(searchItem, 300);
const handleChange = (e) => {
setSearchItem(e.target.value); // this sets value asyncronously
console.log("e.target.value :" + e.target.value); // event.target.value does not omitting last character
console.log("searchItem :" + searchItem); // if we check the value then it is not set. it will update asyncronously
};
const setList = async () => {
if (searchItem === "") {
setListToDisplay([]);
} else {
setListToDisplay(
searchResult.filter((item) => {
return item.name.toLowerCase().includes(searchItem.toLowerCase());
})
);
}
};
const searchRepo = async () => {
const { data } = await axios.get("https://api.github.com/repositories");
setSearchResult(data);
setLoading(false);
};
// this useeffect execute its call back when searchItem changes a
useEffect(() => {
setList(); // called here to use previous value stored in 'searchResult' and display something ( uncomment it if you want to display only updated value )
if (searchItem) searchRepo();
}, [searchItem]);
// this useeffect execute when axios set fetched data in 'searchResult'
useEffect(() => {
setList();
}, [searchResult]);
// this useeffect execute when data is updated in 'listToDisplay'
useEffect(() => {
console.log("filtered Data") // final 'listToDisplay' will be availble here
console.log(listToDisplay)
}, [listToDisplay]);