【问题标题】:Dropdown Item not making api call on first try下拉项在第一次尝试时未进行 api 调用
【发布时间】:2021-01-11 14:32:05
【问题描述】:

我有一个问题,当我选择一个新的下拉项目时,该项目没有保存或没有进行 api 调用,直到我再次选择该项目。因此,如果我从下拉菜单中选择销售并刷新旧值,旧值仍然存在。我已经安慰了状态的值(权限和 adminID),一旦我选择了一个下拉项,它们都被设置为状态,所以我不确定是什么导致它在第一次尝试时没有设置。


const UserCard = (props) => {
  const [dropdownOpen, setDropdownOpen] = useState(false);
  const [permission, setPermission] = useState()
  const [adminID, setAdminID] = useState()

    const item = props.item;
    const toggle = () => {
      setDropdownOpen(prevState => !prevState)
    };
    const adminUpdate = (perm, id) => {
      setPermission(perm)
      if(!permission || !adminID){
        return
      }else{
        api.UserManagement.put(
          adminID,
          {permissions: [permission]}
        ).catch(err => {
          console.log("error", err, err.status)
        })
      }
    }
console.log(permission, adminID)
  return (
    <>    
      <tr key={item.id}>
        <td>{item.name || "-"}</td>
        <td>{item.email}</td>
        <td>{!permission ? item.permissions : permission}</td>
        <td>
        <Dropdown style={{ fontSize: "30px",borderColor: "white", backgroundColor: "white", color: "gray"}} isOpen={dropdownOpen} toggle={toggle}>
        <DropdownToggle
        tag="span"
        data-toggle="dropdown"
        aria-expanded={dropdownOpen}
      >
          ...
      </DropdownToggle>
      <DropdownMenu onClick={() => setAdminID(item.id)} key={item.id}>
        <DropdownItem value={"Admin"} onClick={e => adminUpdate(e.target.value)}>Admin</DropdownItem>
        <DropdownItem value={"Sales"} onClick={e => adminUpdate(e.target.value)}>Sales</DropdownItem>
        <DropdownItem value={"Medical"} onClick={e => adminUpdate(e.target.value)}>Medical</DropdownItem>
      </DropdownMenu>
        </Dropdown>
        </td> 
      </tr>
    </>
  );
};

const UserList = ({}) => {
  const [list, setList] = useState([]);
  const [errors, setErrors] = useState();

  useEffect(() => {
    api.UserManagement.list()
      .then((result) => {
        let list = [];
        if (result) {
          list = result.items;
        }
        setList(list);
      })
      .catch((err) => {
        if (err.status) {
          console.log("error", err, err.status);
          setErrors([err]);
        }
      });
  }, []);


  return (
    <>
      <ContentRow>
        <Row>
          <Col md="8">
            <h1 style={{ marginTop: "25px" }}>
              <Link to={"/"}>
                <Button color="link">&lt;</Button>
              </Link>
              <span style={{ fontSize: "25px" }}>User Management</span>
            </h1>
          </Col>
          <div
            className="d-flex justify-content-around align-items-stretch"
            style={{ marginTop: "15px" }}
          >
            <Link to={"/invitations"}>
              <Button className="header-button" block color="primary">
                Invite Users
              </Button>
            </Link>
          </div>
          <Col md="4"></Col>
        </Row>
      </ContentRow>
      <ContentRow>
        <ErrorList errors={errors}/>
        <Table>
          <thead>
            <tr>
              <th width="30%">User</th>
              <th width="30%">Email Address</th>
              <th width="30%">Role</th>
              <th width="10%"></th>
            </tr>
          </thead>
          <tbody>
            {!list ? (
              <PageLoadSpinner />
            ) : (
              list.map((item) => <UserCard item={item} />)
            )}
          </tbody>
        </Table>
      </ContentRow>
    </>
  );
};

export default UserList;

【问题讨论】:

    标签: reactjs drop-down-menu state use-state


    【解决方案1】:

    原因

    由于useState钩子使用不当,代码无法正常工作。 useState 本质上是异步。因此,当您使用变量时,变量不会更新为正确的值。当前代码假设变量以同步方式更新。

    • setPermission(perm) 在一行中完成,permission 变量在下一行中使用。但此时不会更新权限变量。
    • admin 变量似乎也是如此。

    这就是为什么代码第一次不起作用(变量没有更新)。但是当您再次单击时,会选择较旧的值;条件满足;让它发挥作用。
    阅读更多关于它的信息here

    修复

    有两种方法可以修复此代码。

    方法一

    在 if 条件下,可以直接使用函数参数,而不是使用状态中的变量。
    相关的固定代码为:

    const UserCard = (props) => {
        ...
        const adminUpdate = (perm, id) => {
          if(!perm || !id){ // Change this condition here
            return
          }else{
            api.UserManagement.put(
              id,
              {permissions: [perm]} // Change here as well
            ).catch(err => {
              console.log("error", err, err.status)
            })
          }
        }
    
      return (
        <>    
          ...
          </DropdownToggle>
          <DropdownMenu onClick={() => setAdminID(item.id)} key={item.id}>
            // Pass id onClick of DropdownItem also
            <DropdownItem value={"Admin"} onClick={e => adminUpdate(e.target.value, item.id)}>Admin</DropdownItem> 
            <DropdownItem value={"Sales"} onClick={e => adminUpdate(e.target.value, item.id)}>Sales</DropdownItem> 
            <DropdownItem value={"Medical"} onClick={e => adminUpdate(e.target.value, item.id)}>Medical</DropdownItem> 
          </DropdownMenu>
            </Dropdown>
          ...
        </>
      );
    };
    

    这种方法几乎消除了useState钩子的使用,因为您直接使用从函数传递的变量

    方法2

    您还可以将useEffect 钩子与useState 一起使用。 useEffect 将依赖数组作为第二个参数,并在任何变量更改时触发函数调用。因此,逻辑将分为两部分:

    1. 变量将由 useState 单独更新
    2. 在 useEffect 中更新任何变量时都会触发 API 调用。

    相关的固定代码为:

    const UserCard = (props) => {
      const [dropdownOpen, setDropdownOpen] = useState(false);
      const [permission, setPermission] = useState()
      const [adminID, setAdminID] = useState()
    
        const item = props.item;
        const toggle = () => {
          setDropdownOpen(prevState => !prevState)
        };
      useEffect(() => {
        if (permission && adminID) {
            api.UserManagement.put(
              adminID,
              {permissions: [permission]}
            ).catch(err => {
              console.log("error", err, err.status)
            })
        }
    }, [permission, adminID]) // This will trigger the function call when any of these 2 variables are modified.
    
      return (
        <>    
          ...
          </DropdownToggle>
          // setAdminID on click
          <DropdownMenu onClick={() => setAdminID(item.id)} key={item.id}>
            // setPermission onClick
            <DropdownItem value={"Admin"} onClick={e => setPermission(e.target.value)}>Admin</DropdownItem>
            <DropdownItem value={"Sales"} onClick={e => setPermission(e.target.value)}>Sales</DropdownItem>
            <DropdownItem value={"Medical"} onClick={e => setPermission(e.target.value)}>Medical</DropdownItem>
          </DropdownMenu>
            </Dropdown>
            </td> 
          </tr>
        </>
      );
    };
    

    方法 1 与方法 2

    您可以选择哪种方法。两者都会为您带来结果,但基本方法不同。方法 1 是在单击下拉项时直接触发 API 调用,但方法 2 是在单击下拉项时更新变量,这些变量的更改会触发 API 调用。

    希望对您有所帮助。如有任何疑问/澄清,请回复。

    【讨论】:

      猜你喜欢
      • 2013-09-21
      • 2012-11-14
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 2020-06-10
      • 1970-01-01
      相关资源
      最近更新 更多