我一直在尝试实现一种方法,您可以通过切换更改状态的select元素,使组件重新呈现,从而以不同的方式对排行榜进行排序。
问题在于,它可以正确排序默认值,但是每当我将select的值从默认值更改为“ z-to-a”时,它似乎都没有更新。
注意:我添加了一些console.log语句,这些语句似乎很奇怪。
我的JSX:
import React, { useState, useEffect } from 'react';
import './Leaderboard.css';
import LbRow from '../../components/LbRow/LbRow'; /* A row in the leaderboard*/
import points from '../../data/tree-points.json';
function Leaderboard() {
// Initialize the points as the data that we passed in
const [state, setState] = useState({
points: points,
sortBy: "first-to-last"
});
// Changes the sort method used by the leaderboard
const changeSortBy = (event) => {
var newSort = event.target.value;
// Sorts the data differently depending on the select value
switch(newSort) {
case "first-to-last":
sortDescending("points","first-to-last");
break;
case "z-to-a":
sortDescending("tree_name","z-to-a");
console.log(state.points.treePoints); // Logs incorrectly, still logs the same array as in "first-to-last"
break;
default:
sortDescending("points","first-to-last");
}
// Re-renders the component with new state
setState({
points: state.points,
sortBy: newSort
});
}
/* Updates the leaderboard state to be in descending point order */
const sortDescending = (aspect, sortMethod) => {
console.log(sortMethod); // Logs correctly
// Sorts the data in descending points order
let sortedPoints = [...state.points.treePoints].sort((tree1, tree2) => {
if (tree1[aspect] > tree2[aspect]) { return -1; }
if (tree1[aspect] < tree2[aspect]) { return 1; }
return 0;
});
// Actually updates the state
setState({
points: {
...state.points,
treePoints: sortedPoints
},
sortBy: sortMethod
});
console.log(sortedPoints); // Logs correctly
};
/* Calls sortLb on component mount */
useEffect(() =>{
sortDescending("points", "first-to-last");
}
,[]);
// Attributes used for rendering the leaderboard body
var rank = 0;
const sortedData = state.points;
/* Basically all the active trees with the first tree having the top rank */
const lbBody = sortedData.treePoints.map((sortedData) => {
return (
sortedData.active &&
<LbRow rank={++rank} tree_name={sortedData.tree_name} points={sortedData.points} active={sortedData.active}/>
);
});
return (
<div>
<div className="filters">
{/* Allows user to sort by different methods */}
<label htmlFor="sortBy">Sort by:</label>
<select name="sortBy" className="sortBy" value={state.sortBy} onChange={changeSortBy}>
<option value="first-to-last">First to Last</option>
<option value="z-to-a">Z to A</option>
</select>
</div>
{/* The table with sorted content */}
<div className="table">
{lbBody}
</div>
</div>
);
}
export default Leaderboard;
我真的对这种行为感到困惑,特别是因为我拥有正确排序的值并且据称已经更新了状态。是什么导致这种情况发生?谢谢
答案 0 :(得分:1)
您必须注意三件事
现在,由于您希望调用状态更新程序两次,因此不妨使用回调方法,该方法将确保不会合并来自多个setState调用的状态值,因为您不需要它们。另外,您必须仅更新您想要的字段
function Leaderboard() {
// Initialize the points as the data that we passed in
const [state, setState] = useState({
points: points,
sortBy: "first-to-last"
});
// Changes the sort method used by the leaderboard
const changeSortBy = (event) => {
var newSort = event.target.value;
// Sorts the data differently depending on the select value
switch (newSort) {
case "first-to-last":
sortDescending("points", "first-to-last");
break;
case "z-to-a":
sortDescending("tree_name", "z-to-a");
break;
default:
sortDescending("points", "first-to-last");
}
// Re-renders the component with new state
setState(prev => ({
...prev,
sortBy: newSort // overrider just sortByField
}));
}
/* Updates the leaderboard state to be in descending point order */
const sortDescending = (aspect, sortMethod) => {
console.log(sortMethod); // Logs correctly
// Sorts the data in descending points order
let sortedPoints = [...state.points.treePoints].sort((tree1, tree2) => {
if (tree1[aspect] > tree2[aspect]) {
return -1;
}
if (tree1[aspect] < tree2[aspect]) {
return 1;
}
return 0;
});
// Actually updates the state
setState(prev => ({
...prev,
points: {
...state.points,
treePoints: sortedPoints
},
}));
};
/* Calls sortLb on component mount */
useEffect(() => {
sortDescending("points", "first-to-last");
}, []);
// Attributes used for rendering the leaderboard body
var rank = 0;
const sortedData = state.points;
...
}
export default Leaderboard;
另一个避免复杂的更好方法是将您的状态分为两个useState
function Leaderboard() {
// Initialize the points as the data that we passed in
const [points, setPoints] = useState(points);
const [sortBy, setSortBy] = useState(sortBy);
// Changes the sort method used by the leaderboard
const changeSortBy = (event) => {
var newSort = event.target.value;
// Sorts the data differently depending on the select value
switch(newSort) {
case "first-to-last":
sortDescending("points","first-to-last");
break;
case "z-to-a":
sortDescending("tree_name","z-to-a");
console.log(state.points.treePoints); // Logs incorrectly, still logs the same array as in "first-to-last"
break;
default:
sortDescending("points","first-to-last");
}
// Re-renders the component with new state
setSortBy(newSort);
}
/* Updates the leaderboard state to be in descending point order */
const sortDescending = (aspect, sortMethod) => {
console.log(sortMethod); // Logs correctly
// Sorts the data in descending points order
let sortedPoints = [...state.points.treePoints].sort((tree1, tree2) => {
if (tree1[aspect] > tree2[aspect]) { return -1; }
if (tree1[aspect] < tree2[aspect]) { return 1; }
return 0;
});
// Actually updates the state
setPoints({
...state.points,
treePoints: sortedPoints
});
console.log(sortedPoints); // Logs correctly
};
/* Calls sortLb on component mount */
useEffect(() =>{
sortDescending("points", "first-to-last");
}
,[]);
// Attributes used for rendering the leaderboard body
var rank = 0;
const sortedData = points;
/* Basically all the active trees with the first tree having the top rank */
const lbBody = sortedData.treePoints.map((sortedData) => {
return (
sortedData.active &&
<LbRow rank={++rank} tree_name={sortedData.tree_name} points={sortedData.points} active={sortedData.active}/>
);
});
return (
<div>
<div className="filters">
{/* Allows user to sort by different methods */}
<label htmlFor="sortBy">Sort by:</label>
<select name="sortBy" className="sortBy" value={sortBy} onChange={changeSortBy}>
<option value="first-to-last">First to Last</option>
<option value="z-to-a">Z to A</option>
</select>
</div>
{/* The table with sorted content */}
<div className="table">
{lbBody}
</div>
</div>
);
}
export default Leaderboard;