Ich möchte, dass der Benutzer eine Liste von Aufgabenelementen sortieren kann. Wenn der Benutzer ein Element aus einem Dropdown-Menü auswählt, wird festgelegt, sortKey
welches eine neue Version von erstellt setSortedTodos
, und der useEffect
Aufruf wird wiederum ausgelöst setSortedTodos
.
Das folgende Beispiel funktioniert genau so, wie ich es möchte, aber eslint fordert mich auf todos
, das useEffect
Abhängigkeitsarray zu erweitern, und wenn ich dies tue, verursacht es eine Endlosschleife (wie zu erwarten).
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]);
Live-Beispiel:
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>
Ich denke, es muss einen besseren Weg geben, der eslint glücklich macht.
sort
Rückruf kann einfach sein: Diesreturn a[sortKey].toLowerCase().localeCompare(b[sortKey].toLowerCase());
hat auch den Vorteil, dass ein Gebietsschemavergleich durchgeführt wird, wenn die Umgebung über angemessene Gebietsschemainformationen verfügt. Wenn Sie möchten, können Sie auch Destrukturierung darauf werfen: pastebin.com/7X4M1XTHeslint
wirft?[<>]
? Stack-Snippets unterstützen React, einschließlich JSX. Hier erfahren Sie, wie Sie eine erstellen . Auf diese Weise können die Leute überprüfen, ob ihre vorgeschlagenen Lösungen nicht das Endlosschleifenproblem haben ...todos
das Abhängigkeitsarray hinzufügen müssenuseEffect
, und Sie können sehen, warum Sie dies nicht tun sollten. :-)Antworten:
Ich würde argumentieren, dass dies bedeutet, dass es nicht ideal ist, so vorzugehen. Die Funktion ist in der Tat abhängig von
todos
. WennsetTodos
es an einer anderen Stelle aufgerufen wird, muss die Rückruffunktion neu berechnet werden, andernfalls werden veraltete Daten verarbeitet.Warum speichern Sie das sortierte Array überhaupt im Status? Sie können
useMemo
die Werte sortieren, wenn sich entweder der Schlüssel oder das Array ändert:Dann
sortedTodos
überall referenzieren .Live-Beispiel:
Code-Snippet anzeigen
Es ist nicht erforderlich, die sortierten Werte im Status zu speichern, da Sie das sortierte Array immer aus dem "Basis" -Array und dem Sortierschlüssel ableiten / berechnen können. Ich würde behaupten, es macht Ihren Code auch leichter verständlich, da er weniger komplex ist.
quelle
useMemo
. Nur eine Nebenfrage, warum nicht.localCompare
in der Sorte verwenden?Der Grund für die Endlosschleife ist, dass die Aufgaben nicht mit der vorherigen Referenz übereinstimmen und der Effekt erneut ausgeführt wird.
Warum überhaupt einen Effekt für eine Klickaktion verwenden? Sie können es in einer Funktion wie der folgenden ausführen:
und auf Ihrem Dropdown machen Sie eine
onChange
.Hinweis zur Abhängigkeit übrigens, ESLint ist richtig! Ihre Todos sind im oben beschriebenen Fall eine Abhängigkeit und sollten in der Liste enthalten sein. Der Ansatz bei der Auswahl eines Artikels ist falsch und daher Ihr Problem.
quelle
data.slice(0)
erstellt die Kopie.setState
da das vorhandene Objekt nicht bearbeitet und daher intern geklont wird. Falsche Formulierung in meiner Antwort, stimmt. Ich werde das bearbeiten.setState
klont keine Daten. Warum denkst du, dass?setState
nichts. Felix und das OP sind korrekt. Sie müssen das Array kopieren, bevor Sie es sortieren können.state
.Was Sie hier tun müssen, ist die funktionale Form von
setState
:Arbeitscodesandbox
Selbst wenn Sie den Status kopieren, um den ursprünglichen nicht zu mutieren, kann nicht garantiert werden, dass Sie den neuesten Wert erhalten, da der Status asynchron ist. Außerdem geben die meisten Methoden eine flache Kopie zurück, sodass Sie möglicherweise trotzdem den ursprünglichen Zustand ändern.
Durch die Verwendung von function wird
setState
sichergestellt, dass Sie den neuesten Wert des Status erhalten und den ursprünglichen Statuswert nicht mutieren.quelle
.sort
Ändert das Array an Ort und Stelle, sodass Sie es immer noch selbst kopieren müssen.