React、console.log、renderは異なる値を表示します
コンソールに記録されたオブジェクトがHTMLに表示されているものと一致していないようです。以下の、この簡単な例に戸惑っています。ここでボタンは配列の順序を切り替え、Reactは前のリストをレンダリングしているようです。
===アップデート3 ====
キュプライトはオブジェクトの思いやりでありsetState(list=>list.push(12))、リストを変更しますが、リストのIDが同じであるため、setStateをトリガーしません。
状態フックを現在の状態と同じ値に更新すると、Reactは子をレンダリングしたり、エフェクトを起動したりせずにベイルアウトします。(ReactはObject.is比較アルゴリズムを使用します。)React useState doc
===アップデート2 ====
私は解決策を見つけたと思います。原因はオブジェクトの可変性であるように思われます。回避策は、SolutionSandboxでソートされたリストのディープコピーを作成することです。私は、リストの可変性は、の違いが発生することはありませんので、私は、リアクトの未定義の動作を発見したと考えているconsole.log(list)し、<h1>list</h1>次の図に示します。ReactJSの状態配列の正しい変更
===アップデート1 ====
以下に示すものと同じコードを含むサンドボックス。
私のソート機能に問題はありません!昇順の場合は、小さい方の要素が最初に来るはず(e1,e2)=>e1-e2です。これは、比較関数が正しいことを意味します。
compareFunction(a、b)が0未満を返す場合は、aをbよりも低いインデックスに並べ替えます(つまり、aが最初になります)。比較機能Mozilla
const {useState, useEffect, useReducer} = React;
function App() {
const [list, setList] = useState([0,1]);
const [isAscending, toggle] = useReducer(isAscending=>!isAscending, true);
// update the order of the list
useEffect(()=>{
if (isAscending) {
setList(list.sort((e1,e2)=>e1-e2));
} else {
setList(list.sort((e1,e2)=>e2-e1));
}
},[list, isAscending]);
// render
return (
<div>
{console.log("list[0] should be",list[0])}
<button onClick={toggle}>{isAscending?"Ascending":"Descending"}</button>
<h1>list[0]: {list[0]}</h1>
</div>
)
}
ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>
回答
MDNによると、javascriptの並べ替え関数は配列を変更します。
sort()メソッドは、配列の要素をインプレースでソートし、ソートされた配列を返します。
あなたが書くとき:
setList(list.sort((e1, e2) => e1 - e2));
setListがリストを変更する前に、リストを変更しています。
setListは、指定された値が前の値と異なるかどうかを確認してからレンダリングします。
ただし、リスト値はすでにsortメソッドによって更新されているため、setListに違いはありません。
このため、レンダリングは一歩遅れているようです。
だからあなたは書くべきです:
setList([...list].sort((e1, e2) => e1 - e2));
もう一つ:
私はあなたが変えるべきだと思います
{isAscending ? "Ascending" : "Descending"}
に
{isAscending ? "Descending" : "Ascending"}
トグルボタンなので
最初の注文は昇順です。ボタンをクリックすると、最初に移動する順序を昇順から降順に切り替えます。
つまり、
if (isAscending) {
// Logic for descending should come here
setList(list.sort((e1, e2) => e2 - e1));
} else {
// Logic for ascending should come here
setList(list.sort((e1, e2) => e1 - e2));
}
ここのコードをチェックして、これがあなたが探していたものであるかどうか私に知らせてください。コードサンドボックス:並べ替えられたリストがレンダリングされない
コールバックでlist値をクロージャしuseEffectているので、それをdep配列に追加する必要があります。
function App() {
const [list, setList] = useState([0, 1]);
const [isAscending, toggle] = useReducer((isAscending) => !isAscending, true);
useEffect(() => {
setList(list.sort((e1, e2) => (!isAscending ? e1 - e2 : e2 - e1)));
}, [list, isAscending]);
return (
<div>
<button onClick={toggle}>
{isAscending ? "Ascending" : "Descending"}
</button>
<pre>{JSON.stringify(list)}</pre>
</div>
);
}
持っている
eslint場合は警告が表示されることに注意してください。
https://codesandbox.io/s/react-template-forked-08eft?file=/index.js:99-684
スニペットを少し簡略化しました。
あなたは奇妙なことに両方useStateを使用してuseReducerいましたが、2番目は最初の代替です。
複数のサブ値を含む複雑な状態ロジックがある場合、または次の状態が前の状態に依存している場合は、通常、useReducerの方がuseStateよりも適しています。
Reactjsドキュメントから引用
const {useReducer} = React;
function App() {
const list=[0,1]
const [isAscending, toggle] = useReducer(isAscending=>{
if (isAscending) {
list.sort((e1,e2)=>e2-e1)
} else {
list.sort((e1,e2)=>e1-e2)
}
return !isAscending
}, list);
// render
return (
<div>
{console.log("list[0] should be",list[0])}
<button onClick={toggle}>{isAscending?"Ascending":"Descending"}</button>
<h1>list[0]: {list[0]}</h1>
</div>
)
}
ReactDOM.render(<App/>, document.querySelector('.App'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div class='App'/>
これlistがinitialStateドキュメントの例です。そして、リストを並べ替えるロジックは、今では本当にシンプルになっていますが、レデューサー関数内にあります。
それが役立つことを願っています!;)