Scenario -
Default Input number in text field - 1
Listed Items -
Now as soon as I remove 1 from the text field -
List Items -
Now enter 4 in the input field.
List Items -
Expected Behavior -
List Item should display only - 4, 9 and 14
instead of NaN, NaN, 4, 9, 14
.
Let me know what I am doing wrong here.
Code -
import React, { useState } from "react";
import List from "./List";
const ReRendering = () => {
const [number, setNumber] = useState(1);
const getList = () => [number, number + 5, number + 10];
return (
<div>
<input
type="number"
value={number}
onChange={(e) => setNumber(parseInt(e.target.value, 10))}
/>
<div>
<List getList={getList} />
</div>
</div>
);
};
export default ReRendering;
List.js
import { useEffect, useState } from "react";
const List = ({ getList }) => {
const [item, setItem] = useState([]);
useEffect(() => {
setItem(getList());
}, [getList]);
return (
<div className="list">
{item.map((x) => (
<div key={x}>{x}</div>
))}
</div>
);
};
export default List;
Working Example - https://codesandbox.io/s/gallant-gagarin-l18ni1?file=/src/ReRendering.js:0-463
Doing setNumber(parseInt(e.target.value, 10))
will set the number state to NaN
if the input field is empty, because the empty string can't be parseInt
d.
console.log(parseInt(''));
And NaN causes problems because, as the React warning says:
Warning: Encountered two children with the same key,
NaN
. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
Duplicate NaNs cause duplicate keys.
Instead, use valueAsNumber
, and alternate with 0 if the value doesn't exist.
const { useState, useEffect } = React;
const ReRendering = () => {
const [number, setNumber] = useState(1);
const getList = () => [number, number + 5, number + 10];
return (
<div>
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.valueAsNumber || 0)}
/>
<div>
<List getList={getList} />
</div>
</div>
);
};
const List = ({ getList }) => {
const [item, setItem] = useState([]);
useEffect(() => {
setItem(getList());
}, [getList]);
return (
<div className="list">
{item.map((x) => (
<div key={x}>{x}</div>
))}
</div>
);
};
ReactDOM.render(<ReRendering />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
A better way to structure this would be to call getList
immediately in the child, instead of using a separate state and effect hook inside it.
const { useState, useEffect } = React;
const ReRendering = () => {
const [number, setNumber] = useState(1);
const getList = () => [number, number + 5, number + 10];
return (
<div>
<input
type="number"
value={number}
onChange={(e) => setNumber(e.target.valueAsNumber || 0)}
/>
<div>
<List getList={getList} />
</div>
</div>
);
};
const List = ({ getList }) => (
<div className="list">
{getList().map((x) => (
<div key={x}>{x}</div>
))}
</div>
);
ReactDOM.render(<ReRendering />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>