2

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?

4
  • could you reproduce this on codesandbox? Commented Aug 5, 2020 at 17:06
  • @hgb123, I'll try, and post up a link. Not sure how I'll do the API call though. Commented Aug 5, 2020 at 17:18
  • you could mock the data by json file Commented Aug 5, 2020 at 17:29
  • @hgb123here you go: codesandbox.io/s/rough-dream-331qc Commented Aug 5, 2020 at 18:12

1 Answer 1

3

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

Edit compassionate-galileo-nkz71

Sign up to request clarification or add additional context in comments.

1 Comment

Whoops, I forgot about those, I usually rename my variables before posting online, just in case. In any case, thanks, the 'fetched' really did it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.