React:如何使用下拉菜单动态排序对象数组(使用 React Hooks)
假设我们有以下问题:
- 对对象数组进行排序
- 根据不同的属性值动态地执行
- 使用react.js在浏览器中呈现
好的,让我们开始谈正事吧!
对象示例数组:
const bands = [
{
name: 'Nightwish',
albums: 9,
members: 6,
formed_in: 1996,
},
{
name: 'Metallica',
albums: 10,
members: 4,
formed_in: 1981,
},
{
name: 'Nirvana',
albums: 3,
members: 3,
formed_in: 1987,
},
];
为了本教程的目的,我不会做任何花哨的组件,所以让我们在普通的div中呈现这个数组。
function App() {
return (
<div className="App">
{bands.map(band => (
<div key={band.id} style={{ margin: '30px' }}>
<div>{`Band: ${band.name}`}</div>
<div>{`Albums: ${band.albums}`}</div>
<div>{`Members: ${band.members}`}</div>
<div>{`Year of founding: ${band.formed_in}`}</div>
</div>
))}
</div>
);
}
检查浏览器中的视图:
看起来不错!
现在让我们添加具有可排序属性选项的选择元素。
<select>
<option value="albums">Albums</option>
<option value="members">Members</option>
<option value="formed">Formed in</option>
</select>
这太棒了,但是当我们改变下拉选项时什么也没有发生。
为了解决这个问题,我们需要以某种方式连接选择元素和我们想要排序的数组,并在每次选择不同的选择选项时重新呈现排序后的数组值。
根据React 的文档:
默认情况下,当你的组件的状态或道具发生变化时,你的组件将重新渲染。
这意味着我们必须向组件添加状态。我将借助React Hooks来完成此操作。
让我们使用useState hook定义状态变量数据及其更新方法setData。
const [data, setData] = useState([]);
假设,当状态将使用新数据(已排序的数组)更新时,组件应该重新渲染。为了测试它,我们需要定义一个函数,该函数将根据下拉菜单中的选定选项对 bands 数组进行排序,并在每次选定选项发生变化时调用它。
...
const sortArray = type => {
const types = {
albums: 'albums',
members: 'members',
formed: 'formed_in',
};
const sortProperty = types[type];
const sorted = bands.sort((a, b) => b[sortProperty] - a[sortProperty]);
console.log(sorted);
setData(sorted);
};
...
<select onChange={(e) => sortArray(e.target.value)}>
...
但是当我们运行代码时,它不能正常工作。
数组排序正常,控制台里打印了结果,但数组数据并没有重新渲染。只有当我们第一次更改排序后的值时,它才会重新渲染。
问题如下,正如React 文档中所述:
不要直接修改状态
所以,这行代码是错误的,因为它就地修改了 state(对当前 state 中的数组进行排序) 。而 React “认为” setData调用的是它已经拥有的同一个数组,因此不会重新渲染。(非常感谢TJ Crowder帮我澄清了这个问题)
const sorted = bands.sort((a, b) => b[sortProperty] - a[sortProperty]);
正确的方法是先复制 bands 数组,对其进行排序,然后使用该数组调用setData 方法。因此,只需在复制数组时添加扩展运算符即可解决问题。
const sorted = [...bands].sort((a, b) => b[sortProperty] - a[sortProperty]);
我们试着运行一下代码。嗯,代码能运行,但是一开始,也就是在 select 选项改变之后,bands 数据并没有渲染出来。
借助useEffect Hook 可以轻松解决这个问题。
1.定义另一个状态变量来存储 sort 属性的值。默认情况下,bands 数组将按专辑数量排序。
const [sortType, setSortType] = useState('albums');
2.在选择选项改变时更新sortType的值。
<select onChange={(e) => setSortType(e.target.value)}>
3. 添加useEffect Hook,它将在组件渲染后调用sortArray函数,并在每次更新sortType值时调用。我们通过将第二个参数 ( sortType ) 传递给useEffect来实现这一点,该参数是 effect 所依赖的值数组。
useEffect(() => {
const sortArray = type => {
const types = {
albums: 'albums',
members: 'members',
formed: 'formed_in',
};
const sortProperty = types[type];
const sorted = [...bands].sort((a, b) => b[sortProperty] - a[sortProperty]);
setData(sorted);
};
sortArray(sortType);
}, [sortType]);
现在代码可以按预期工作了!
完整的源代码可以在这个 GitHub 仓库中找到
最初发布在我自己的博客上
链接:https://dev.to/ramonak/react-how-to-dynamically-sort-an-array-of-objects-using-the-dropdown-with-react-hooks-195p