1

Complete newbie here, it's my first time trying to make an app. I chose Next.js as my framework since it is one of the most popular and recommened by React now. The code below simply initialises a small list, and lets you add stuff to the list with a text box and "add" button. The tutorial I followed used javascript, but I wanted to do it in typescript. My question is regarding passing the hooks from the default function into the consts down below. I initially had:

const List = ( tasks : {id: string; task: string}[]; )

Since that is the data type of tasks, an array of elements where each element has an id and a task. However it wouldn't accept it. I was required to add an additional layer which baffels me:

const List = ( tasks : {tasks : {id: string; task: string}[];} )

Doing this then means that when I use the map function, I have to go tasks.tasks.map, rather than just tasks.map, and doing this doesn't make a whole lot of sense to me. It definitely seems like there should be a better way. The input to the "AddTask" const also seems quite verbose, is there a way I can make that less of a nightmare to look at?

'use client';
import { useState } from "react";
import { v4 as uuidv4 } from 'uuid';

const initialTasks = [
  {
    id: 'a',
    task: 'Make this app',
  },
  {
    id: 'b',
    task: 'Do uni work',
  },
];

export default function Home() {
  const [tasks, setTasks] = useState(initialTasks);
  const [text, setText] = useState("");

  function addToList() {
    const newTask = {id: uuidv4(), task: text};
    setTasks([...tasks, newTask]);
    setText("");
  }

  function handleChange(event: any) {
    setText(event.target.value);
  }

  return (
    <div>
      <h1>Task Manager</h1>
      <p>To Do List:</p>
      <List tasks={tasks} />

      <AddTask 
        text={text}
        onChange={handleChange}
        onAdd={addToList}
      />
    </div>
  )
}

const List = ( tasks : {tasks : {id: string; task: string}[];} ) => (
  <ul>
    {tasks.tasks.map((task: any) => (
      <li key={task.id}>{task.task}</li>))
    }
  </ul>
);

const AddTask = ({ text, onChange, onAdd }: {text: string; onChange: (event: any) => void; onAdd: () => void}) => (
  <div>
    <input type="text" value={text} onChange={onChange} />
    <button type="button" onClick={onAdd}>Add</button>
  </div>
);
kelsny
  • 23,009
  • 3
  • 19
  • 48
Scott
  • 21
  • 1

1 Answers1

0

The first argument passed to components is the props object. Yours expects a tasks prop, ie props.tasks hence the need to define an object with a tasks property.

I would strongly suggest you avoid using inline types and instead, define interfaces and types with meaningful names

interface Task {
  id: string;
  task: string;
}

interface ListProps {
  tasks: Task[];
}

then you can use them where appropriate

const [tasks, setTasks] = useState<Task[]>(initialTasks);

and

const List = ({ tasks }: ListProps) => (
  <ul>
    {tasks.map(({ id, task }) => (
      <li key={id}>{task}</li>
    ))}
  </ul>
);

// or without destructuring

const List = (props: ListProps) => (
  <ul>
    {props.tasks.map((task) => (
      <li key={task.id}>{task.task}</li>
    ))}
  </ul>
);
Phil
  • 157,677
  • 23
  • 242
  • 245
  • I see! That does make it a lot more concise. After experimenting with your code, I figured out that the curley brackets around the { tasks } is what is allowing the simple tasks.map. In your code or mine, if those curley brackets are deleted, you need to use tasks.tasks.map. Why is that? Sorry if my questions are very simple. – Scott Mar 20 '23 at 04:18
  • @Scott see [What is destructuring assignment and its uses?](https://stackoverflow.com/a/54605288/283366), example #5. Further reading ~ [ES6 In Depth: Destructuring](https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/) – Phil Mar 20 '23 at 04:43