简体   繁体   English

我应该使用 Redux 商店将数据从孩子传递给父母吗?

[英]Should I use the Redux store to pass data from child to parent?

I am building a to-do/notes app in order to learn the basics of Redux, using React hooks and Typescript.我正在构建一个待办事项/笔记应用程序,以便使用 React 挂钩和 Typescript 学习 Redux 的基础知识。

A note is composed of an ID and a value.注释由 ID 和值组成。 The user can add, delete or edit a note.用户可以添加、删除或编辑注释。

The add / delete mechanics work fine.添加/删除机制工作正常。 But the edit one is trickier for me, as I'm questionning how it should be implemented.但是编辑对我来说比较棘手,因为我在质疑应该如何实施它。

I think my reducer's code is fine.我认为我的减速器代码很好。 The problem lies between my component (Note.tsx) and its parent one (App.tsx).问题出在我的组件 (Note.tsx) 和它的父组件 (App.tsx) 之间。

When i'm logging the value, I can see that the new updated/edited value of the note is not sent to the reducer.当我记录值时,我可以看到注释的新更新/编辑值没有发送到减速器。 As a result, my note is not edited with the new value.结果,我的笔记未使用新值进行编辑。

I've tried "cloning" the redux store and making my changes here, but it seems tedious and unnatural to me.我试过“克隆”redux 商店并在此处进行更改,但对我来说这似乎乏味且不自然。 Should I just call the edit method from my Note.tsx component?我应该只从我的 Note.tsx 组件调用编辑方法吗?

Is there a clean / conventional way to do this?有没有一种干净/传统的方法来做到这一点?

Here is my code:这是我的代码:

App.tsx应用程序.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>
  );
}

Note.tsx笔记.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 && <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;

and the notesReducer.tsx和 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;
  }
};

Thanks to @secan in the comments I made this work, plus some changes.感谢 @secan 在我的评论中做出了这项工作,并进行了一些更改。

In App.tsx:在 App.tsx 中:

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

In Note.tsx:在 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 && <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 })}
      />
    );
  };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM