1

I write a React.js note web application where a user can add up to 10 notes.

I use map() to iterate the array of notes, and a useState(1) hook to update its count (the default number of notes is 1), so I would like to do something like this:

  {[...Array(noteCount)].map((_, i) => <Note onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}

The thing is that the Note() component is inside a Main() component which is in the App() component, so I want to get the needed values as props of App(), and than use them in Note(), but can not figure out how and where to put it.

Thanks!

App.js

  import React from 'react';
  import Header from './Header';
  import Main from './Main';
function App () {
  const [noteCount, setNoteCount] = React.useState(1);
  function multiplyNoteComponent () {
    if (noteCount < 20) {
      setNoteCount(n => n + 1)
    }
    else {
      alert('too many notes. remove or combine some of them together!')
    }
  }
  return (
    <div>
      <Header/>
      {[...Array(noteCount)].map((_, i) => <Main onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}

      <button
            style={{left: '5%'}}
          id='addNoteBtn'
          onClick={multiplyNoteComponent}
          title='Add a note'
         >
        +
      </button>
    </div>
  );
}
  export default App;

Main.js

    import React from 'react';
    import Note from './Note';
function Main () {
    return (
        <main>
            your notes are:
            <Note/>
        </main>
    )
}
    export default Main;

Note.js

    import React from 'react';
function Note () {
    return (
        <div> <button title='delete note' onClick={}>X</delete>
            <li>
                <input type='text'/>
            </li>
        </div>
    )
}
    export default Note

Edit: the reason I think I need the setNoteCount() function to be used in the Note() component, is for the count down when a note is being deleted (every note has its own delete button).

  • You can pass props from App -> Main -> Note like explained here : https://stackoverflow.com/a/59540647/5427320 – Atif Saddique Sep 14 '20 at 14:41
  • Maybe `App` should pass `Note`s as children to `Main`. I'm not quite following we you are keep track of the *number* of notes. You need to save the text somewhere, right? Why not just have an array of strings? – Felix Kling Sep 14 '20 at 14:44
  • @AtifSaddique Thanks, but I think I need to pass the setNoteCount() somehow into Note.js(), don't I? :) –  Sep 14 '20 at 14:49
  • If you need that `setNoteCount` in your child component, then you can pass the function as prop as well to child component, but I don't think you should pass the overall noteCount set function to individual Note, the parent should handle the set note count. – Atif Saddique Sep 14 '20 at 14:53
  • I think you should explain a bit more about your needs, what I understand from your question is that you need to pass some props from App.js to Note.js, which can be done by passing props from App.js to Main.js and then from there to Note.js – Atif Saddique Sep 14 '20 at 14:56

1 Answers1

0

I would recommend this architecture of the your App.

  1. Store the Notes array at the App level.
  2. Add a note using NoteInput which adds a notes to your Notes array.
  3. Map your Notes using the Note component which takes onDelete as a prop from App level.
  4. Your App component should be responsible for storing and delete a note from the state.

In your example, notesCount is meant to a derivative state. i.e it could be derived simply from the Notes array (notes.length).

So, rather than storing notesCount, I recommend storing notes and deriving count from it.

You could see the working example here :- https://stackblitz.com/edit/react-g19tei

import React from "react";
import "./style.css";

const NOTES_ALLOWED = 10;

export default function App() {
  const [notes, setNotes] = React.useState([]);

  function addNote(newNote) {
    if (notes.length === NOTES_ALLOWED) {
      alert(`Only ${NOTES_ALLOWED} notes are allowed to be added`)
    } else {
      setNotes([...notes, newNote]);
    }
  }

  function handleDelete(deleteNoteIdx) {
    const newNotes = [...notes];
    // delete the note at the specific index
    newNotes.splice(deleteNoteIdx, 1)
    setNotes(newNotes);
  }

  return (
    <div>
      <div style={{ marginTop: 20, marginBottom: 20 }}>
        <p>Your notes are</p>
        {notes.map((note, idx) => ( 
          <Note 
            note={note} 
            onDelete={() => handleDelete(idx)} 
          />
        ))}
      </div>
      <NoteInput onAdd={addNote} />
    </div>
  );
}

function Note({ note, onDelete }) {
  return (
    <div>
     <p>{note}
      <button onClick={onDelete}>Delete Note</button>
     </p>
    </div>
  )
}

function NoteInput({ onAdd }) {
  const [note, setNote] = React.useState('');

  function handleSubmit(e) {
    e.preventDefault();
    const noteToBeSend = note;
    setNote('')
    onAdd(noteToBeSend.trim());
  }

  return (
    <div>
     <form onSubmit={handleSubmit}>
        <input 
          type="text" 
          value={note}
          onChange={e => setNote(e.target.value)}
          required
        />
        <button type="submit">Add Note</button>
      </form>
    </div>
  )
}

Prateek Thapa
  • 4,829
  • 1
  • 9
  • 23
  • Thanks mate, your answer + reading the documentations of react props * 5 helped a lot! I just wondered, is a prop that's transferred to a grandchild is a reason to start using Redux? –  Sep 19 '20 at 07:26
  • In the current scenario of React, there is no need to use Redux though. You could simply use the [Context API](https://reactjs.org/docs/context.html). The only difference between using Redux and the Context API would be performance is optimized with Redux. – Prateek Thapa Sep 19 '20 at 07:28