简体   繁体   中英

Why React Re rendering is not updating the returned value of JSX?

import { useState, useEffect } from 'react';
import './App.css';

const App = () => {

    const [data, setData] = useState([]);
    const [pages, setPages] = useState(0);
    const [currentPage, setCurrentPage] = useState(1);

    const ITEMS_PER_PAGE = 10;
    const slicedData = data.slice(currentPage * ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE);

    const getData = () => {
        fetch("https://jsonplaceholder.typicode.com/comments")
            .then(response => response.json())
            .then(json => {
                console.log(json)
                setPages(json.length / 50);
                setData(json);
            });
    }

    const clickNextHandler = () => {
        if (currentPage < pages) {
            setCurrentPage(page => page + 1);
        }
    }

    const clickPreviousHandler = () => {
        if (currentPage > 1) {
            setCurrentPage(page => page - 1);
        }
    }

    const changePageHandler = (e) => {
        setCurrentPage(e)
    }

    console.log('Sliced Data ', slicedData)

    const sortHandler = () => {
        setData(data => data.sort((a, b) => a.email.localeCompare(b.email)));
    }

    const btns = [];
    btns.push(<button onClick={clickNextHandler}>Next</button>)
    for (let i = 1; i <= pages; i++) {
        btns.push(
            <button
                key={i}
                className={currentPage === i ? "active" : ""}
                onClick={() => changePageHandler(i)}>
                {i}
            </button>
        )
    }
    btns.push(<button onClick={clickPreviousHandler}>Previous</button>)

    useEffect(() => {
        getData();
    }, [])

    return (
        <div className="App">
            {btns}
            <h2>{slicedData.length ? (slicedData[0].email) : ""}</h2>
            <h2 onClick={sortHandler}>Data Table</h2>
            <div className="table">
                {slicedData.map((each, idx) => {
                    return <p key={idx}>{each.email}</p>
                })}
            </div>
        </div>
    )
}
export default App;

When clicked on "Data Table", sortHandler sorts the data, re-rendering also happens. Console shows that slicedData value is also changing (Sorting happened). But still the returned value of JSX remains the same. Its mapping over slicedData array, but its not getting updated or sorted when re-rendering happens when clicked on "Data Table". Could someone help why sorted data is not appearing ?

Using index as key prop will always result in buggy behavior when you're trying to sort or filter rows. You're using idx as a key here

{slicedData.map((each, idx) => {
    return <p key={idx}>{each.email}</p>
})}

Try using some unique identifier for each element. Changing the key prop of the p to some unique identifier other than index will fix it.

React uses keys to detect what items in a list have changed. Since you're using index as a key in non-static instances, and later sort the array out from which you're rendering, the order of the indices remain the same.

Also, you're mutating the state array directly .

setData(data => data.sort((a, b) => a.email.localeCompare(b.email)));

Inside sortHandler function, you're applying sort on data , which is your state array. Doing so mutates the state array directly, which also causes bugs like these. You should consider creating a shallow copy of the array so that you can operate on it and then set its value as the updated data array. Something like this:

const sortHandler = () => {
    setData((data) =>
      data.slice().sort((a, b) => a.email.localeCompare(b.email))
    );
  };

Here's the link to working CodeSandBox

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM