Home > OS >  Why my re-render page after filter doesn't working?
Why my re-render page after filter doesn't working?

Time:10-15

in my project I show a list where all the pokemons categories are.

When the user clicks on a certain category the list is updated.

My list is updating, but the problem is that my component is not re-rendering again with the new list items.

Here's my code I put into enter image description here

import React from "react";

import { types, pokemons } from "./data";

import Avatar from "./components/Avatar";
import List from "./components/List";

import "./styles.css";

const App = () => {
  const [favorite, setFavorite] = React.useState("rock");

  console.log(favorite);

  const _data = [];
  React.useMemo(
    () =>
      pokemons.map((pokemon, i) => {
        if (pokemon.type.includes(favorite.toLowerCase())) {
          _data.push(pokemon);
        }
        return _data;
      }),
    [_data, favorite]
  );

  const removeDup = [];
  _data.reduce((acc, curr) => {
    if (acc.indexOf(curr.name) === -1) {
      acc.push(curr.name);
      removeDup.push(curr);
    }
    return acc;
  }, []);

  return (
    <div className="App">
      <Avatar data={types} setFavorite={setFavorite} />
      <List data={removeDup} />
    </div>
  );
};

export default App;

List

const List = ({ data }) => {
  const [pokemonsState, setPokemonsState] = useState(data);
  const [isAscSort, setIsAscSort] = useState(false);

  const sortPokemon = () => {
    if (isAscSort)
      setPokemonsState(stableSort(data, getComparator("asc", "name")));
    else setPokemonsState(stableSort(data, getComparator("desc", "name")));

    setIsAscSort(!isAscSort);
  };

  return (
    <TableContainer>
      <Table sx={{ minWidth: 650 }}>
        <TableHead>
          <TableRow>
            <TableCell>Pokémon</TableCell>

            <TableCell name onClick={() => sortPokemon()} align="right">
              Name
              {!isAscSort ? <ArrowUpward /> : <ArrowDownward />}
            </TableCell>
          </TableRow>
        </TableHead>
        <TableBody>
          {pokemonsState.map((pokemon, idx) => (
            <TableRow
              key={idx}
              sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
            >
              <TableCell component="th" scope="row" thumbnailImage>
                <div className="thumb">
                  <img src={pokemon.thumbnailImage} alt="" />
                </div>
              </TableCell>
              <TableCell align="left" component="th" scope="row" description>
                {pokemon.name}
              </TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};

Could you tell me what I'm doing wrong?

Thank you very much in advance!!!

CodePudding user response:

Issues

You've at least a couple issues.

  1. Using the mapped array index as the React key is generally an anti-pattern, especially if you are filtering, sorting, mutating the underlying array being mapped.
  2. The List component doesn't update its pokemonsState state when the data prop updates.

Solution

Use a useEffect with a dependency on the data prop to update the local pokemonsState state. use the pokemon.id as the React key, assuming all pokemon have unique id properties.

const List = ({ data }) => {
  const [pokemonsState, setPokemonsState] = useState(data);
  const [isAscSort, setIsAscSort] = useState(false);

  // Update local state when prop updates
  useEffect(() => {
    setPokemonsState(data);
  }, [data]);

  const sortPokemon = () => {
    if (isAscSort)
      setPokemonsState(stableSort(data, getComparator("asc", "name")));
    else setPokemonsState(stableSort(data, getComparator("desc", "name")));

    setIsAscSort(!isAscSort);
  };

  return (
    <TableContainer>
      <Table sx={{ minWidth: 650 }}>
        ...
        <TableBody>
          {pokemonsState.map((pokemon, idx) => (
            <TableRow
              key={pokemon.id} // <-- use unique React key
              sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
            >
              ...
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </TableContainer>
  );
};

Edit why-my-re-render-page-after-filter-doesnt-working

CodePudding user response:

Your List component is trying to copy the data prop into its state. Copying props into state is usually a bad idea. If that prop changes, the List will ignore the change and continue using its state. Only once something changes the state (eg, clicking on the sort button) will you get back in sync.

I would recommend that you delete the state and instead compute the sorted list from the prop. This computation can be put inside of useMemo to improve performance by skipping calculating if nothing has changed:

const List = ({ data }) => {
  const [isAscSort, setIsAscSort] = useState(false);
  const sortedPokemons = useMemo(() => {
    if (isAscSort) {
      return stableSort(data, getComparator("asc", "name"))
    } else {
      return stableSort(data, getComparator("desc", "name"))
    }
  }, [data, isAscSort]);

  const sortPokemon = () => {
    setIsAscSort(!isAscSort);
  };

  // ...
  <TableBody>
    {sortedPokemons.map((pokemon, idx) => (
      // ...
    )}
  </TableBody>
}
  • Related