Как использовать функцию useState в другом компоненте с реквизитом?

Я пишу веб-приложение для заметок React.js, в котором пользователь может добавить до 10 заметок.

Я использую map() для итерации массива заметок и хук useState(1) для обновления его количества (количество заметок по умолчанию равно 1), поэтому я хотел бы сделать что-то вроде этого:

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

Дело в том, что компонент Note() находится внутри компонента Main(), который находится в компоненте App(), поэтому я хочу получить необходимые значения в качестве реквизита App(), а затем использовать их в Note(), но не может понять, как и куда его поместить.

Спасибо!

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

Изменить: причина, по которой я думаю, что мне нужна функция setNoteCount() для использования в компоненте Note(), заключается в обратном отсчете при удалении заметки (каждая заметка имеет свою собственную кнопку удаления).


person matantan    schedule 14.09.2020    source источник
comment
Вы можете передать реквизиты из App -> Main -> Note, как описано здесь: stackoverflow.com/a/59540647/5427320   -  person Atif Saddique    schedule 14.09.2020
comment
Возможно, App следует передать Notes как дочерние элементы Main. Я не совсем понимаю, что мы должны отслеживать количество заметок. Вам нужно где-то сохранить текст, верно? Почему бы просто не иметь массив строк?   -  person Felix Kling    schedule 14.09.2020
comment
@AtifSaddique Спасибо, но я думаю, мне нужно как-то передать setNoteCount() в Note.js(), не так ли? :)   -  person matantan    schedule 14.09.2020
comment
Если вам нужен этот setNoteCount в вашем дочернем компоненте, вы можете передать функцию в качестве реквизита дочернему компоненту, но я не думаю, что вы должны передавать общую функцию набора noteCount отдельной заметке, родитель должен обрабатывать установленное количество заметок .   -  person Atif Saddique    schedule 14.09.2020
comment
Я думаю, вам следует немного подробнее рассказать о своих потребностях. Из вашего вопроса я понимаю, что вам нужно передать некоторые реквизиты из App.js в Note.js, что можно сделать, передав реквизиты из App.js в Main.js. а оттуда в Note.js   -  person Atif Saddique    schedule 14.09.2020


Ответы (1)


Я бы рекомендовал эту архитектуру вашего приложения.

  1. Сохраните массив Notes на уровне App.
  2. Добавьте заметку, используя NoteInput, которая добавляет заметки в ваш массив Notes.
  3. Сопоставьте свой Notes с помощью компонента Note, который использует onDelete в качестве реквизита уровня App.
  4. Ваш компонент App должен отвечать за сохранение и удаление заметки из состояния.

В вашем примере notesCount означает производное состояние. то есть его можно получить просто из массива Notes (notes.length).

Таким образом, вместо того, чтобы хранить notesCount, я рекомендую хранить заметки и получать из них счет.

Вы можете увидеть рабочий пример здесь: - 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>
  )
}

person Prateek Thapa    schedule 17.09.2020