Saya ingin pengguna dapat mengurutkan daftar item todo. Ketika pengguna memilih item dari dropdown itu akan mengatur sortKey
yang akan membuat versi baru setSortedTodos
, dan pada gilirannya memicu useEffect
dan memanggil setSortedTodos
.
Contoh di bawah ini berfungsi persis seperti yang saya inginkan, namun eslint mendorong saya untuk menambahkan todos
ke useEffect
array dependancy, dan jika saya lakukan itu menyebabkan loop tak terbatas (seperti yang Anda harapkan).
const [todos, setTodos] = useState([]);
const [sortKey, setSortKey] = useState('title');
const setSortedTodos = useCallback((data) => {
const cloned = data.slice(0);
const sorted = cloned.sort((a, b) => {
const v1 = a[sortKey].toLowerCase();
const v2 = b[sortKey].toLowerCase();
if (v1 < v2) {
return -1;
}
if (v1 > v2) {
return 1;
}
return 0;
});
setTodos(sorted);
}, [sortKey]);
useEffect(() => {
setSortedTodos(todos);
}, [setSortedTodos]);
Contoh Langsung:
const {useState, useCallback, useEffect} = React;
const exampleToDos = [
{title: "This", priority: "1 - high", text: "Do this"},
{title: "That", priority: "1 - high", text: "Do that"},
{title: "The Other", priority: "2 - medium", text: "Do the other"},
];
function Example() {
const [todos, setTodos] = useState(exampleToDos);
const [sortKey, setSortKey] = useState('title');
const setSortedTodos = useCallback((data) => {
const cloned = data.slice(0);
const sorted = cloned.sort((a, b) => {
const v1 = a[sortKey].toLowerCase();
const v2 = b[sortKey].toLowerCase();
if (v1 < v2) {
return -1;
}
if (v1 > v2) {
return 1;
}
return 0;
});
setTodos(sorted);
}, [sortKey]);
useEffect(() => {
setSortedTodos(todos);
}, [setSortedTodos]);
const sortByChange = useCallback(e => {
setSortKey(e.target.value);
});
return (
<div>
Sort by:
<select onChange={sortByChange}>
<option selected={sortKey === "title"} value="title">Title</option>
<option selected={sortKey === "priority"} value="priority">Priority</option>
</select>
{todos.map(({text, title, priority}) => (
<div className="todo">
<h4>{title} <span className="priority">{priority}</span></h4>
<div>{text}</div>
</div>
))}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("root"));
body {
font-family: sans-serif;
}
.todo {
border: 1px solid #eee;
padding: 2px;
margin: 4px;
}
.todo h4 {
margin: 2px;
}
.priority {
float: right;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
Saya pikir harus ada cara yang lebih baik untuk melakukan ini yang membuat eslint bahagia.
sort
Callback bisa adil:return a[sortKey].toLowerCase().localeCompare(b[sortKey].toLowerCase());
yang juga memiliki keunggulan melakukan perbandingan lokal jika lingkungan memiliki informasi lokal yang masuk akal. Jika Anda suka, Anda juga dapat melakukan penghancuran padanya: pastebin.com/7X4M1XTHeslint
melempar?[<>]
tombol bilah alat)? Stack Snippets mendukung React, termasuk JSX; inilah cara melakukannya . Dengan begitu orang dapat memeriksa bahwa solusi yang mereka tawarkan tidak memiliki masalah loop tak terbatas ...todos
ke array dependensiuseEffect
, dan Anda dapat melihat mengapa Anda tidak perlu. :-)Jawaban:
Saya berpendapat bahwa ini berarti bahwa melakukannya dengan cara ini tidak ideal. Fungsi ini memang tergantung
todos
. JikasetTodos
dipanggil di tempat lain, fungsi callback harus dihitung ulang, jika tidak beroperasi pada data basi.Mengapa Anda menyimpan array yang disortir dalam keadaan? Anda bisa menggunakan
useMemo
untuk mengurutkan nilai saat kunci atau array berubah:Lalu referensi
sortedTodos
kemana-mana.Contoh Langsung:
Tampilkan cuplikan kode
Tidak perlu menyimpan nilai yang diurutkan dalam keadaan, karena Anda selalu dapat menurunkan / menghitung array yang diurutkan dari array "base" dan kunci sortir. Saya berpendapat itu juga membuat kode Anda lebih mudah dipahami karena kurang rumit.
sumber
useMemo
. Hanya pertanyaan sampingan, mengapa tidak digunakan.localCompare
dalam pengurutan?Alasan untuk infinite loop adalah karena todos tidak cocok dengan referensi sebelumnya dan efeknya akan dijalankan kembali.
Mengapa tetap menggunakan efek untuk tindakan klik? Anda dapat menjalankannya dalam fungsi seperti ini:
dan pada dropdown Anda lakukan
onChange
.Perhatikan ketergantungannya, ESLint benar! Todos Anda, dalam kasus yang dijelaskan di atas, adalah ketergantungan dan harus ada dalam daftar. Pendekatan pada pemilihan item salah, dan karenanya masalah Anda.
sumber
data.slice(0)
membuat salinan.setState
karena itu tidak akan mengedit objek yang ada dan karenanya akan mengkloningnya secara internal. Kata-kata yang salah dalam jawaban saya, benar. Saya akan mengedit ini.setState
tidak mengkloning data. Mengapa kamu berpikir begitu?setState
tidak mengkloning apa pun. Felix dan OP sudah benar, Anda perlu menyalin array sebelum mengurutkannya.state
.Yang perlu Anda lakukan di sini adalah menggunakan bentuk fungsional
setState
:Kode dan kotak kerja
Bahkan jika Anda menyalin status untuk tidak mengubah yang asli, itu masih tidak dijamin bahwa Anda akan mendapatkan nilai terbaru, karena pengaturan negara menjadi async. Plus, sebagian besar metode akan mengembalikan salinan yang dangkal, jadi Anda mungkin akan bermutasi pada kondisi semula.
Menggunakan fungsional
setState
memastikan bahwa Anda mendapatkan nilai negara terbaru dan tidak mengubah nilai negara asal.sumber
.sort
mengubah array di tempat, jadi Anda masih harus menyalinnya sendiri.