Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
114 views
in Technique[技术] by (71.8m points)

reactjs - Dropdown Item not making api call on first try

Im having an issue where when I select a new Dropdown Item the Item isnt saved or the api call isnt made until I select the item again. So if I select Sales from the the Dropdown menu and and refresh the old value will still be there. Ive consoled out the value of the state (permission and adminID) and as soon as I select a dropdown item they are both set to the state so Im not sure whats causing it not to set on the first try.


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;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Reason

The code is not working due to improper usage of useState hook. useState is asynchronous in nature. Due to this, the variables are not updated with proper values when you are using it. The current code is assuming variables to be updated in synchronous manner.

  • setPermission(perm) is done in one line and permission variable is used in next line. But permission variable won't be updated at that time.
  • Same seems for admin variable.

That's why for the first time, the code doesn't work (the variables are not updated). But when you click again, the older values are picked; condition is satisfied; making it work.
Read more about it here.

Fix

There can be two ways in which this code can be fixed.

Approach 1

In the if condition, you can use the function arguments directly instead of variable from use state.
The relevant fixed code will be:

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>
      ...
    </>
  );
};

This approach almost removes the usage of useState hook as you are directly using variables passed from function

Approach 2

You can also use useEffect hook along with useState. useEffect takes dependency array as second argument and will trigger function call when any of the variable is changed. So, the logic will be splitted into 2 parts:

  1. Variables will be updated separately by useState
  2. Api call will be triggered when any of variable is updated in useEffect.

The relevant fixed code will be:

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>
    </>
  );
};

Approach 1 vs Approach 2

It's upto you which approach to chose from. Both will get you the result but differ in base approach. Approach 1 is triggering the API call directly on click of dropdown item but Approach 2 is updating the variables on click of dropdown items and the change of those variables is triggering the API call.

Hope it helps. Revert for any doubts/clarifications.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...