简体   繁体   中英

React prevent from toggle multiple modals

I have problem with my react app. I have generated list of 100 items. I display every element from list using map function

<ul className="list">
    {this.props.items.map((item) => {
      return (
        <ItemView
          key={item.id}
          id={item.id}
          name={item.name}
          strenght={item.strenght}
          age={item.age}
        />
      );
    })}
</ul>

My item view display name, age and strenght. On mouseEnter display button which after click display modal under clicked element.

class ItemView extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isMouseInside: false,
      isModalOpen: false,
    };
  }

  mouseEnter = () => {
    this.setState({ isMouseInside: true });
  };

  mouseExit = () => {
    this.setState({ isMouseInside: false });
  };

  showModal = () => {
    this.setState({ isModalOpen: !this.state.isModalOpen });
  };

  closeModal = () => {
    this.setState({ isModalOpen: false });
  };

  render() {
    return (
      <li
        onMouseEnter={this.mouseEnter}
        onMouseMove={this.mouseEnter}
        onMouseLeave={this.mouseExit}
      >
        <div className="item__row">
           <h5>{this.props.name}</h5>
           <h6>Age: {this.props.age}</h6>
           <p>{this.props.strenght}/100</p>

          {this.state.isMouseInside ? (
            <button className="btn" onClick={() => this.showForm(false)}>
              Change data
            </button>
          ) : null}

        </div>
        {this.state.isModalOpen ? (
          <Modal
            onSubmit={this.handleSubmitForm}
          />
        ) : null}
      </li>
    );
  }
}

What I want to do is to prevent from blocking the possibility of opening two modals or even better if after click on another button in another itemView, opened modal will be closed and suitable will open.

What happened now is I can open multiple modals and everythink works, but I want to make one modal will be displayed on page.

I tried to make another function closeModal which set state of isModalOpen to false before this.setState({ isModalOpen: !this.state.isModalOpen }) in showModal function but it doesn't work.

In this app I use redux and if redux can help solve the problem I can use it.

Visualization what i want to do

CODEPEN EXAMPLE: https://codepen.io/dominik3246/pen/QqKzzp

Here is how I refactored your code:

  • Downgraded ItemView Class component to container/dump-componenet. Remember, the fewer class components you have the better.
  • When the button is clicked, I am storing the ID of the current item clicked in currentItemId
  • Then I am showing the modal only if the state currentItemId matches the current item ID

I've tried my utmost to use React's pattern in the refactoring. In fact, I reduced LOC to 59 from 85 and the code IMO looks much neater now.

Use this code as a reference on how can code your React component's and containers in the future as your code needed some polishing.

Codepen: https://codepen.io/metju/pen/LzRqJp

Code:

class List extends React.Component {
    constructor() {
        super()
        this.state = {
            currentItemId: undefined,
            data: [{id: 0, name: "item1", strenght: 5, age: 5}, {id: 1, name: "item2", strenght: 5, age: 5}, {
                id: 2,
                name: "item1",
                strenght: 5,
                age: 5
            }, {id: 3, name: "item1", strenght: 5, age: 5}, {id: 4, name: "item1", strenght: 5, age: 5}],
        };
        this.toggleModalFunc = this.toggleModalFunc.bind(this);
    }

    toggleModalFunc(id) {
        const currentItemId = id !== this.state.currentItemId ? id : undefined
        this.setState({ currentItemId })
    };

    render() {
        return (
            <ul className="list">
                {this.state.data.map((d) => {
                    return (
                        <ItemView
                            key={d.id}
                            name={d.name}
                            strenght={d.strenght}
                            age={d.age}
                        >
                            <button className="btn" itemId={d.id} onClick={() => this.toggleModalFunc(d.id)}>
                                Change data
                            </button>
                            {this.state.currentItemId === d.id ? <Modal /> : null}
                        </ItemView>
                    );
                })}
            </ul>
        );
    }
}
const Modal = () => (
    <div>
        THIS IS MODAL
    </div>
)
const ItemView = (props) => {
    return <li>
        <div className="item__row">
            <h5>{props.name}</h5>
            <h6>Age: {props.age}</h6>
            <p>{props.strenght}/100</p>
            {props.children[0]}
        </div>
        {props.children[1]}
    </li>
}

ReactDOM.render(<List />, document.getElementById('app'));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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