下拉项在第一次尝试时未进行 api 调用
Dropdown Item not making api call on first try
我遇到一个问题,当我 select 一个新的下拉项目时,该项目没有保存,或者 api 调用没有进行,直到我再次 select 该项目。因此,如果我从下拉菜单中 select Sales 并刷新旧值仍然存在。我已经安慰了状态的值(权限和 adminID),并且当我 select 一个下拉项时,它们都被设置为状态,所以我不确定是什么导致它在第一次尝试时没有设置。
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"><</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;
原因
由于 useState
挂钩使用不当,代码无法正常工作。 useState
本质上是 异步 。因此,当您使用它时,变量不会更新为正确的值。当前代码假设变量以同步方式更新。
setPermission(perm)
在一行中完成,permission
变量在下一行使用。但是此时权限变量不会更新。
admin
变量似乎也是如此。
这就是为什么第一次,代码不起作用(变量没有更新)。但是当你再次点击时,旧的值被选中;条件满足;让它发挥作用。
阅读更多相关信息 。
修复
修复此代码的方法有两种。
方法一
在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
挂钩的使用,因为您直接使用从函数
传递的变量
方法二
您还可以将 useEffect
钩子与 useState
一起使用。 useEffect 将依赖数组作为第二个参数,并在任何变量更改时触发函数调用。因此,逻辑将分为两部分:
- 变量将由useState单独更新
- Api 当 useEffect 中的任何变量更新时都会触发调用。
相关固定代码为:
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 调用。
希望对您有所帮助。还原任何 doubts/clarifications.
我遇到一个问题,当我 select 一个新的下拉项目时,该项目没有保存,或者 api 调用没有进行,直到我再次 select 该项目。因此,如果我从下拉菜单中 select Sales 并刷新旧值仍然存在。我已经安慰了状态的值(权限和 adminID),并且当我 select 一个下拉项时,它们都被设置为状态,所以我不确定是什么导致它在第一次尝试时没有设置。
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"><</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;
原因
由于 useState
挂钩使用不当,代码无法正常工作。 useState
本质上是 异步 。因此,当您使用它时,变量不会更新为正确的值。当前代码假设变量以同步方式更新。
setPermission(perm)
在一行中完成,permission
变量在下一行使用。但是此时权限变量不会更新。admin
变量似乎也是如此。
这就是为什么第一次,代码不起作用(变量没有更新)。但是当你再次点击时,旧的值被选中;条件满足;让它发挥作用。
阅读更多相关信息
修复
修复此代码的方法有两种。
方法一
在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
挂钩的使用,因为您直接使用从函数
方法二
您还可以将 useEffect
钩子与 useState
一起使用。 useEffect 将依赖数组作为第二个参数,并在任何变量更改时触发函数调用。因此,逻辑将分为两部分:
- 变量将由useState单独更新
- Api 当 useEffect 中的任何变量更新时都会触发调用。
相关固定代码为:
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 调用。
希望对您有所帮助。还原任何 doubts/clarifications.