2

I'd like to know what's the best pattern to use in the following use case:
I have a list of items in my ItemList.js

  const itemList = items.map((i) => <Item key={i}></Item>);

  return (
      <div>{itemList}</div>
  )

Each of this Items has an 'EDIT' button which should open a dialog in order to edit the item.
Where should I put the Dialog code?

  1. In my ItemList.js => making my Item.js call the props methods to open the dialog (how do let the Dialog know which Item was clicked? Maybe with Redux save the id of the item inside the STORE and fetch it from there?)
  2. In my Item.js => in this way each item would have its own Dialog

p.s. the number of items is limited, assume it's a value between 5 and 15.

3 Answers 3

1

You got a plenty of options to choose from:

  1. Using React 16 portals

This option let you render your <Dialog> anywhere you want in DOM, but still as a child in ReactDOM, thus maintaining possibility to control and easily pass props from your <EditableItem> component.

  1. Place <Dialog> anywhere and listen for special app state property, if you use Redux for example you can create it, place actions to change it in <EditableItem> and connect.

  2. Use react context to send actions directly to Dialog, placed on top or wherever.

Personally, i'd choose first option.

Sign up to request clarification or add additional context in comments.

Comments

0

You can have your <Dialog/> as separate component inside application's components tree and let it to be displayed in a case if your application's state contains some property that will mean "we need to edit item with such id". Then into your <Item/> you can just have onClick handler that will update this property with own id, it will lead to state update and hence <Dialog/> will be shown.

2 Comments

Do you have any recommendations on how to pass props we need to use in that dialog except id? For example, onClose handler, context values, etc?
@PavloZhukov Sorry, I'm not working with React these days. It is better to publish separate question
0

UPDATED to better answer the question and more completely tackle the problem. Also, followed the suggestion by Pavlo Zhukov in the comment below: instead of using a function that returns functions, use an inline function.

I think the short answer is: The dialog code should be put alongside the list. At least, this is what makes sense to me. It doesn't sound good to put one dialog inside each item.

If you want to have a single Dialog component, you can do something like:

import React, { useState } from "react";
import "./styles.css";

const items = [
  { _id: "1", text: "first item" },
  { _id: "2", text: "second item" },
  { _id: "3", text: "third item" },
  { _id: "4", text: "fourth item" }
];

const Item = ({ data, onEdit, key }) => {
  return (
    <div key={key}>
      {" "}
      {data._id}. {data.text}{" "}
      <button type="button" onClick={onEdit}>
        edit
      </button>
    </div>
  );
};

const Dialog = ({ open, item, onClose }) => {
  return (
    <div>
      <div> Dialog state: {open ? "opened" : "closed"} </div>
      <div> Dialog item: {JSON.stringify(item)} </div>
      {open && (
        <button type="button" onClick={onClose}>
          Close dialog
        </button>
      )}
    </div>
  );
};

export default function App() {
  const [isDialogOpen, setDialogOpen] = useState(false);
  const [selectedItem, setSelectedItem] = useState(null);

  const openEditDialog = (item) => {
    setSelectedItem(item);
    setDialogOpen(true);
  };

  const closeEditDialog = () => {
    setDialogOpen(false);
    setSelectedItem(null);
  };

  const itemList = items.map((i) => (
    <Item key={i._id} onEdit={() => openEditDialog(i)} data={i} />
  ));

  return (
    <>
      {itemList}
      <br />
      <br />
      <Dialog
        open={isDialogOpen}
        item={selectedItem}
        onClose={closeEditDialog}
      />
    </>
  );
}

(or check it directly on this CodeSandbox)

2 Comments

Frankly speaking, this answer doesn't bring any useful info on how to open dialog, just and advice that we can use carrying to simplify id passing on activating behavior. However, in our commercial project, we prefer using inline arrow functions. And even taking an event together with business logic looks like a mixing of concerns. The only exception might be if you need to prevent that event from multiple triggers, but it's a rare case in my practice. IMHO onEdit={() => this.openEditDialog(i)} looks better in this case.
Hi Pavlo. I wasn't worried on how to open a dialog as that was not the question asked. However, I expanded the example and took your suggestion on a better looking onEdit function. I agree it not only looks better but seems cleaner. Hope this is useful. Regards.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.