Reputation: 4376
Following this example of nested arrays, I'm trying to display the following data:
{ array: [
{
{ "last_received_date": "2020-08-03 11:04:18 UTC+8", "tasks": [
{ "id": "1", "freq": "d", "prog": 0, "max": 5, "reward": 1000 },
{ "id": "2", "freq": "d", "prog": 0, "max": 1, "reward": 1000 },
{ "id": "3", "freq": "d", "prog": 0, "max": 3, "reward": 1000 }]
}
},
{ everything like above but more }
]}
This is a long one, and I'm not well-versed in react hooks myself, so please bear with me. My problem is that the fields
in my nested field returns an empty array, if probed or queried any further. Here's a link to a sandbox I made (as far as I could emulate it).
Here's the nested field:
export default function TaskFields({ nestIndex, control, register, errors }) {
const { fields, remove } = useFieldArray({ control,
name: `array[${nestIndex}].challenges`
});
const indexMap = ['Daily', 'Weekly'];
return (
<Box>
{fields.map((task, j) => { // Should repeat thrice, but fields is apparently empty
return (
<Box key={j}>
<Button variant="danger" onClick={() => {remove(j);}} size="small" }>x</Button>
ID: <input name={indexMap[nestIndex] + `task[${j}].id`} ref={register()} defaultValue={task.id} />
Freq: // Same as above
Prog: // Same as above
Max Prog: // Same as above
Reward: // Same as above
</Box> );
})}
</Box> );
}
Notice the name: array[${nestIndex}].challenges
part; if displayed via console.log
, it returns an empty array. If I remove the [${nestIndex}].challenges
from it, it returns the complete data set (the value of array
). But that outer part of the data has already been dealt with, and displayed, here:
The primary field:
export default function TaskTypeCards({ control, register, errors }) {
const { fields } = useFieldArray({ control, name: "array" });
const indexMap = ['Daily', 'Weekly'];
return (
<Box>
{fields.map((item, i) => { // Will repeat twice, since there are two objects in the array
return (
<Box key={i}>
Title: {indexMap[i]}
Last Received Date: // This will display the last received date
<TaskFields data={item} nestIndex={i} {...{ control, register, errors }}/>
// This should display the array of objects in 'tasks'
</Box>
)
})}
</Box> );
}
The above works fine, and displays two boxes with the Last Received Date
, meaning the fields
there retained the data I needed from the form, below:
export default UserTaskForm (data) {
const [challengeData, setChallengeData] = useState({});
const { register, control, handleSubmit, setError, reset, errors } = useForm(challengeData);
useEffect(() => {
async function fetchData() {
let result = await dbGetUserTasks(data.userId); // Gets results from API
let challengeData = JSON.parse(result[0].data)); // Looks like the data above
setChallengeData(challengeData);
reset(challengeData);
}
fetchData();
}, [reset]);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Button type="submit" style={{ marginLeft: "auto" }}>Save Changes</Button>
{challengeData ? <TaskTypeCards {...{ control, register, errors }} /> : null}
</form> );
}
So what I'm left with is two boxes with the Last Received Data
, but nothing of the tasks that are listed therein, which is far from the example I linked above.
I've tried replacing the array[${nestIndex}].challenges
with just array
in the name
of TaskFields, and then replaced the fields.map
to fields[nestIndex].challenges.map
which does work in displaying the data, but the delete button does deletes the entire object under array, not an object under tasks, presumably because fields
there is set to array.
Is there anything I missed?
Upvotes: 2
Views: 4765
Reputation: 14891
I saw two things to notice here
First is that the nested is tasks
not challenges
// UserTaskFields.js
// ...
const { fields, remove } = useFieldArray({
control,
name: `array[${nestIndex}].tasks`,
})
// ...
Second, you used challengeData
with default state of empty object {}
to conditionally render if the object is not empty. But you might not remember that Boolean({}) === true
, so that you accidentally render from the beginning, even before fetching data. So a solution might be using Object.keys(challengeData).length > 0
instead, or just use a boolean value of fetched
for simplicity
const x = {}
console.log(Boolean(x))
console.log(x ? 'foo' : 'bar')
// UserTaskForm.js
const [challengeData, setChallengeData] = useState({});
const [fetched, setFetched] = useState({});
// ...
// This will always render because Boolean({}) return true
{challengeData ? <TaskTypeCards {...{ control, register, errors }} /> : null}
// => should be
{Object.keys(challengeData).length > 0 ? <TaskTypeCards {...{ control, register, errors }} /> : null}
// => other way
{fetched? <TaskTypeCards {...{ control, register, errors }} /> : null}
Below is my forked codesandbox with the fix
Upvotes: 3