LirysJH
LirysJH

Reputation: 214

Force update to make functional component re-render

I'm doing pokedex (pokemon wiki stuff). I want to change my component view, when clicking on pokemon images (description lookalike). When I click on an image - nothing happens (firstly, I want at least pokemon's name to be added to the pokemonDescription array). What am I doing wrong?

    let pokemonDescription = [];

    const useForceUpdate = () => {
        const [value, setValue] = useState(true);
        return () => setValue(value => !value);
    }
    const forceUpdate = useForceUpdate();

    const onPokemonClick = (event) => {
        console.log(
            "wrapper clicked, event.target - ",
            event.target.getAttribute('data-name')
        );
        pokemonDescription = [];
        pokemonDescription.push(event.target.getAttribute('data-name'));
        console.log("description array -", pokemonDescription);

        forceUpdate();
    };

    useEffect(() => {
        document.querySelector(".wrapper").addEventListener("click", onPokemonClick);

        ...

        return () => {
            document.querySelector(".wrapper").removeEventListener("click", onPokemonClick);
        };
    }, []);

    ...

    return (
        <div className="Pokemons">
            <div className="column pokemons-list">
                <div className="wrapper">
                    {
                        pokemonsData.map((p, id) => (
                            <div className="box" key={ id }>
                                <img
                                    src={ p.sprites.front_default }
                                    alt="pokemon-img"
                                    title={ p.name }
                                    className="icon"
                                    data-name={p.name}
                                />
                                { p.name}
                                <div className="container">
                                    { pokemonsTypes[id] }
                                </div>
                            </div>
                        ))
                    }
                </div>

                ...

            </div>
            <div className="column description">
                { pokemonDescription }
            </div>
        </div>
    )

Upvotes: 0

Views: 138

Answers (3)

Drew Reese
Drew Reese

Reputation: 203542

You should add pokemonDescription to your component state

const [pokemonDescription, setPokemonDescription] = useState([]);

Remove the forceUpdate function and hook, it is unnecessary.

Attach the click handlers to the elements with the data-name attribute you are trying to handle.

Map the pokemonDescription state array to renderable JSX. I simply used a div, but you should use whatever your UI design requires.

const onPokemonClick = (event) => {
  setPokemonDescription(names => [
    ...names,
    event.target.getAttribute('data-name'),
  ]);
};

...

return (
    <div className="Pokemons">
        <div className="column pokemons-list">
            <div className="wrapper">
                {
                    pokemonsData.map((p, id) => (
                        <div className="box" key={ id }>
                            <img
                                src={ p.sprites.front_default }
                                alt="pokemon-img"
                                title={ p.name }
                                className="icon"
                                data-name={p.name}
                                onClick={onPokemonClick} // <-- attach click handler to img element
                            />
                            { p.name}
                            <div className="container">
                                { pokemonsTypes[id] }
                            </div>
                        </div>
                    ))
                }
            </div>

            ...

        </div>
        <div className="column description">
            {pokemonDescription.map(name => (
              <div>{name}</div>
            ))}
        </div>
    </div>
)

Upvotes: 1

سعيد
سعيد

Reputation: 1764

I don't what that useForceUpdate does , but here is how would go about adding pokemon names to description array which is a state variable in my answer

    const [pokemonDescription , setPokemonDescription ] = useState(null);

    const onPokemonClick = (p) => {
        const tempPokemonDescription = [...pokemonDescription ];
        pokemonDescription.push(p.name);
        console.log("description array -", pokemonDescription);
        setPokemonDescription(tempPokemonDescription )
    };

    ...

    return (
        <div className="Pokemons">
            <div className="column pokemons-list">
                <div  className="wrapper">
                    {
                        pokemonsData.map((p, id) => (
                            <div className="box" onClick={e=>onPokemonClick(p)} key={ id }>
                                <img
                                    src={ p.sprites.front_default }
                                    alt="pokemon-img"
                                    title={ p.name }
                                    className="icon"
                                />
                                { p.name}
                                <div className="container">
                                    { pokemonsTypes[id] }
                                </div>
                            </div>
                        ))
                    }
                </div>

                ...

            </div>
            <div className="column description">
                { pokemonDescription }
            </div>
        </div>
    )

Upvotes: 0

Lukas Cizek
Lukas Cizek

Reputation: 200

Add pokemonDescription to state instead of some local variable and it will solve your issue.

Try to avoid using forceUpdate, most of the times it means only that you are doing something silly.

Upvotes: 0

Related Questions