Должен ли я использовать хранилище Redux для передачи данных от дочернего элемента к родительскому?

#javascript #reactjs #redux

#javascript #reactjs #redux

Вопрос:

Я создаю приложение to-do / notes, чтобы изучить основы Redux, используя перехватчики React и Typescript.

Примечание состоит из идентификатора и значения. Пользователь может добавлять, удалять или редактировать заметку.

Механика добавления / удаления работает нормально. Но редактировать для меня сложнее, поскольку я задаюсь вопросом, как это должно быть реализовано.

Я думаю, что код моего редуктора в порядке. Проблема заключается между моим компонентом (Note.tsx) и его родительским компонентом (App.tsx).

Когда я регистрирую значение, я вижу, что новое обновленное / отредактированное значение примечания не отправляется в редуктор. В результате моя заметка не редактируется с новым значением.

Я пытался «клонировать» хранилище redux и вносить свои изменения здесь, но мне это кажется утомительным и неестественным. Должен ли я просто вызвать метод редактирования из моего компонента Note.tsx?

Есть ли чистый / обычный способ сделать это?

Вот мой код :

App.tsx

 function App() {
  const notes = useSelector<NotesStates, NotesStates['notes']>(((state) => state.notes));

  const dispatch = useDispatch();

  const onAddNote = (note: string) => {
    dispatch(addNote(note));
  };

  const onDeleteNote = (note: NoteType) => {
    dispatch(deleteNote(note));
  };

  const onEditNote = (note: NoteType) => {
    dispatch(updateNote(note));
  };

  return (
    <div className="home">
      <NewNoteInput addNote={onAddNote} />
      <hr />
      <ul className="notes">
        {notes.map((note) => (
          <Note
            updateNote={() => onEditNote(note)}
            deleteNote={() => onDeleteNote(note)}
            note={note}
          />
        ))}
      </ul>
    </div>
  );
}
  

Примечание.tsx

 interface NoteProps {
    deleteNote(): void
    updateNote(noteValue: string | number): void
    note: NoteType
}

const Note: React.FC<NoteProps> = ({ deleteNote, updateNote, note: { id, value } }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [newNoteValue, setNewNoteValue] = useState(value);

  const onDeleteNote = () => {
    deleteNote();
  };

  const onUpdateNote = () => {
    updateNote(newNoteValue);
    setIsEditing(false);
  };

  const handleOnDoubleClick = () => {
    setIsEditing(true);
  };

  const renderBody = () => {
    if (!isEditing) {
      return (
        <>
          {!value amp;amp; <span className="empty-text">Note is empty</span>}
          <span>{value}</span>
        </>
      );
    }
    return (
      <input
        value={newNoteValue}
        onChange={(e) => setNewNoteValue(e.target.value)}
        onBlur={onUpdateNote}
      />
    );
  };

  return (
    <li className="note" key={id}>
      <span className="note__title">
        Note n°
        {id}
      </span>

      <div className="note__body" onDoubleClick={handleOnDoubleClick}>
        {renderBody()}
      </div>
      <button type="button" onClick={onDeleteNote}>Delete</button>
    </li>
  );
};

export default Note;
  

и notesReducer.tsx

 export interface NotesStates {
    notes: Note[]
}

export interface Note {
    id: number
    value: string
}

const initialState = {
  notes: [],
};

let noteID = 0;

export const notesReducer = (state: NotesStates = initialState, action: NoteAction): NotesStates => {
  switch (action.type) {
    case 'ADD_NOTE': {
      noteID  = 1;
      return {
        ...state,
        notes: [...state.notes, {
          id: noteID,
          value: action.payload,
        }],
      };
    }
    case 'UPDATE_NOTE': {
      return {
        ...state,
        notes: state.notes.map((note) => {
          if (note.id === action.payload.id) {
            return {
              ...note,
              value: action.payload.value,
            };
          }
          return note;
        }),
      };
    }
    case 'DELETE_NOTE': {
      return {
        ...state,
        notes: [...state.notes
          .filter((note) => note.id !== action.payload.id)],
      };
    }
    default:
      return state;
  }
};
  

Комментарии:

1. в вашем App.tsx у вас есть updateNote={() => onEditNote(note)} который выполняет функцию onEditNote , передающую текущую заметку в качестве аргумента. Вместо этого вы должны иметь updateNote={onEditNote} , и в вашем Note.tsx вы должны заменить onBlur={onUpdateNote} на onBlur={() => onUpdateNote()}

Ответ №1:

Благодаря @secan в комментариях я выполнил эту работу, а также внес некоторые изменения.

В App.tsx :

       <Note
        updateNote={onEditNote}
        deleteNote={() => onDeleteNote(note)}
        note={note}
      />
  

В Note.tsx :

 interface NoteProps {
    deleteNote(): void
    updateNote(newNote: NoteType): void // updated the signature
    note: NoteType
}

// Now passing entire object instead of just the value
    const onUpdateNote = (newNote: NoteType) => {
        updateNote(newNote);
        setIsEditing(false);
      };

const renderBody = () => {
    if (!isEditing) {
      return (
        <>
          {!value amp;amp; <span className="empty-text">Note is empty</span>}
          <span>{value}</span>
        </>
      );
    }
    return (
      <input
        value={newNoteValue}
        onChange={(e) => setNewNoteValue(e.target.value)}
        // modifying current note with updated value
        onBlur={() => onUpdateNote({ id, value: newNoteValue })}
      />
    );
  };