无法保存首次点击状态

时间:2019-11-06 10:13:59

标签: javascript reactjs react-state-management react-state

我正在构建一个React组件,用户可以在其中选择选项,并且一旦用户在选择后单击“保存”,就将选项保存为状态。但是,在第一次单击保存后(在模态内部),不会保存它。关闭模态并再次打开后,最后选择的选项将被保存。

Sample.js

import React, { Component } from "react";

import Modal from "./Modal";

class Sample extends Component {
  constructor(props) {
    super(props);
    this.state = {
      users: [],
      usersUn: [
        { id: 1, name: "kepa" },
        { id: 2, name: "rudiger" },
        { id: 3, name: "alonso" },
        { id: 4, name: "Christensen" },
        { id: 7, name: "Kante" },
        { id: 8, name: "Barkley" },
        { id: 9, name: "Tammy" },
        { id: 10, name: "willian" },
        { id: 11, name: "pedro" },
        { id: 12, name: "Loftus-Cheek" },
        { id: 13, name: "Caballero" },
        { id: 15, name: "Zouma" },
        { id: 18, name: "Giroud" },
        { id: 19, name: "Mount" },
        { id: 20, name: "Hudson-Odoi" },
        { id: 22, name: "Pulisic" },
        { id: 23, name: "Batshuayi" },
        { id: 24, name: "James" },
        { id: 28, name: "azpilicueta" },
        { id: 29, name: "tomori" },
        { id: 33, name: "Emerson" }
      ],
      usersSel: [{ id: 17, name: "kovacic" }, { id: 5, name: "jorginho" }],
      isLoading: false,
      err: null
    };
  }

  save = (itemsLeft, itemsRight) => {
    this.setState({
      usersUn: itemsLeft,
      usersSel: itemsRight
    });
  };

  render() {
    return (
      <Modal
        usersUn={this.state.usersUn}
        usersSel={this.state.usersSel}
        loading={this.state.isLoading}
        err={this.state.err}
        title="Gimme a title"
        leftTitle="Squad"
        rightTitle="PL11"
        save={this.save}
      />
    );
  }
}

export default Sample;

Modal.js

import React, { Component } from "react";
import Popup from "reactjs-popup";

class Modal extends Component {
  constructor(props) {
    super(props);
    this.state = {
      open: false,
      itemsLeft: this.props.usersUn,
      itemsRight: this.props.usersSel,
      selectedLeft: [],
      selectedRight: [],
      filterLeft: "",
      filterRight: ""
    };
  }

  openModal = () => {
    this.setState({ open: true });
  };

  closeModal = () => {
    this.setState({
      open: false,
      itemsLeft: this.props.usersUn,
      itemsRight: this.props.usersSel
    });
  };

  handleChange = e => {
    this.setState({ [e.target.name]: e.target.value });
  };

  selectLeft = id => {
    const selectedVal = this.state.itemsLeft.filter(user => user.id === id);
    // console.log(selectedVal);
    this.setState(prevState => {
      return {
        selectedLeft: [...prevState.selectedLeft, ...selectedVal]
      };
    });
  };

  selectRight = id => {
    const selectedVal = this.state.itemsRight.filter(user => user.id === id);
    // console.log(selectedVal);
    this.setState(prevState => {
      return {
        selectedRight: [...prevState.selectedRight, ...selectedVal]
      };
    });
  };

  moveAllRight = () => {
    this.setState(prevState => {
      return {
        itemsLeft: [],
        itemsRight: [...prevState.itemsRight, ...prevState.itemsLeft]
      };
    });
  };

  moveRight = () => {
    const updatedItemsLeft = this.state.itemsLeft.filter(item => {
      for (var i = 0; i < this.state.selectedLeft.length; i++) {
        if (this.state.selectedLeft[i].id === item.id) return false;
      }
      return true;
    });
    const updatedItemsRight = [
      ...this.state.itemsRight,
      ...this.state.selectedLeft
    ];
    this.setState({
      itemsLeft: updatedItemsLeft,
      itemsRight: updatedItemsRight,
      selectedLeft: []
    });
  };

  moveLeft = () => {
    const updatedItemsRight = this.state.itemsRight.filter(item => {
      for (var i = 0; i < this.state.selectedRight.length; i++) {
        if (this.state.selectedRight[i].id === item.id) return false;
      }
      return true;
    });
    const itemsLeft = [...this.state.itemsLeft, ...this.state.selectedRight];
    this.setState({
      itemsLeft: itemsLeft,
      itemsRight: updatedItemsRight,
      selectedRight: []
    });
  };

  moveAllLeft = () => {
    this.setState(prevState => {
      return {
        itemsRight: [],
        itemsLeft: [...prevState.itemsLeft, ...prevState.itemsRight]
      };
    });
  };

  saveList = () => {
    this.props.save(this.state.itemsLeft, this.state.itemsRight);
    this.closeModal();
  };

  filterItems = (items, filterTxt) => {
    return items.filter(item =>
      item.name.toLowerCase().includes(filterTxt.toLowerCase())
    );
  };

  render() {
    const { loading, err, title, leftTitle, rightTitle } = this.props;
    const { itemsLeft, itemsRight, filterLeft, filterRight } = this.state;

    const filteredDataL = this.filterItems(itemsLeft, filterLeft);
    const filteredDataR = this.filterItems(itemsRight, filterRight);

    if (loading) {
      return <div>Loading...</div>;
    }

    if (err) {
      return <div>{err}</div>;
    }

    return (
      <>
        <button className="button" onClick={this.openModal}>
          Open Modal
        </button>
        <Popup open={this.state.open} modal closeOnDocumentClick>
          <div className="modal__content">
            <div className="modal__header">
              <h4>{title}</h4>
              <button onClick={this.closeModal}>&times;</button>
            </div>
            <div className="modal__body">
              <div>
                <h4>
                  {leftTitle}
                  {`(${itemsLeft.length})`}
                </h4>
                <div>
                  <input
                    type="search"
                    name="filterLeft"
                    placeholder="search"
                    value={filterLeft}
                    onChange={this.handleChange}
                  />
                </div>
                <div className="results">
                  {filteredDataL.map(user => {
                    return (
                      <div
                        className="list__item"
                        key={user.name}
                        onClick={() => this.selectLeft(user.id)}
                      >
                        {user.name}
                        {`(${user.id})`}
                      </div>
                    );
                  })}
                </div>
              </div>
              <div className="controls">
                <button onClick={this.moveAllRight}>&gt;&gt;</button>
                <button onClick={this.moveRight}>&gt;</button>
                <button onClick={this.moveLeft}>&lt;</button>
                <button onClick={this.moveAllLeft}>&lt;&lt;</button>
              </div>
              <div>
                <h4>
                  {rightTitle}
                  {`(${itemsRight.length})`}
                </h4>
                <div>
                  <input
                    type="search"
                    placeholder="search"
                    name="filterRight"
                    value={filterRight}
                    onChange={this.handleChange}
                  />
                </div>
                <div className="results">
                  {filteredDataR.map(user => {
                    return (
                      <div
                        className="list__item"
                        key={user.name}
                        onClick={() => this.selectRight(user.id)}
                      >
                        {user.name}
                        {`(${user.id})`}
                      </div>
                    );
                  })}
                </div>
              </div>
            </div>
            <div className="modal__footer">
              <button onClick={this.closeModal}>Cancel</button>
              <button onClick={this.saveList}>Save</button>
            </div>
          </div>
        </Popup>
      </>
    );
  }
}

export default Modal;

有人可以看一下,看看我在这里想念什么吗?另外,如果有更好的方法可以帮助我。

Codesandbox

注意:目前,我没有为所选项目添加任何视觉体验。用户单击任何项​​目后,它将处于选中状态,并使用> or <按钮可以移动项目。

3 个答案:

答案 0 :(得分:1)

您的saveList方法中的问题。 this.closeModal();行将重置您的值。因此,您只需要更新open状态。 请如下更新。

  saveList = () => {
    this.props.save(this.state.itemsLeft, this.state.itemsRight);
    this.setState({ open: false });
  };

这是您https://codesandbox.io/s/react-multi-select-olqc9的工作演示。

答案 1 :(得分:1)

状态更新是异步的。按下保存按钮时,您会使用当前值更新父状态,然后还会使用通过仍然是旧状态的道具传递的状态更新模态。

答案 2 :(得分:0)

问题来自saveList函数。

当您致电this.props.save时,它将在您的setState父母中执行Sample

setState()是异步的,即使您使用prop调用closeModal,也不会在此时用“ good”值更新prop。

尝试在 openModal()中设置itemLeftsitemsRight

openModal = () => {
    this.setState({
      open: true,
      itemsLeft: this.props.usersUn,
      itemsRight: this.props.usersSel
    });
  };

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