我遇到了这个奇怪的问题,这是我第一次遇到它。我创建了一个使用 Redux 工具包处理应用程序创建的按钮。根据 UI 设计,按钮应该在如下所示的页面上出现两次。突出显示的按钮是同一组件。
如果我尝试创建一个应用程序,它会显示两条 toast 消息:
我注意到,如果我删除“创建应用程序”按钮之一并保留一个,然后我尝试创建一个仅显示一条 Toast 消息的应用程序,如预期的那样。
创建 2 个单独的按钮来处理一项功能是理想的最佳做法吗?
这是 CreateAnApp 按钮:
import React, { useState, useEffect } from "react";
import { Box, Button, Checkbox, FormControl, FormLabel, Flex, Input, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, Spinner, Text, ModalBody, ModalCloseButton, Wrap, Select, Textarea } from "@chakra-ui/react";
import { Select as Select1 } from "chakra-react-select";
import { useToast } from "@chakra-ui/react";
import { useDropzone } from "react-dropzone";
import "./style.css";
import { AiOutlineCloudUpload } from "react-icons/ai";
import { useDispatch, useSelector } from "react-redux";
import { createApp, reset } from "../../features/apps/appSlice";
export const CreateAnApp = (props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const { variant, bg, textColor, fontSize, fontWeight, leftIcon, hover, children, ...rest } = props;
const { isAppLoading, isError, isAppSuccess, message } = useSelector(
(state) => state.app
);
const toast = useToast();
const [formData, setFormData] = useState({
name: "",
displayName: "",
reason: "",
product: "",
environment: "",
});
const { name, displayName, reason, product, environment } = formData;
const [icon, setIcon] = useState([]);
const { getRootProps, getInputProps } = useDropzone({
accept: "image/*",
onDrop: (acceptedFiles) => {
setIcon(
acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
)
);
},
});
// const [product, setProduct] = useState([]);
const [scopes, setScopes] = useState([]);
const [institutionScope, setInstitutionScope] = useState([]);
// const [environment, setEnvironment] = useState([]);
const images = icon.map((file) => (
));
const onChange = (e) => {
setFormData((prevState) => ({
...prevState,
[e.target.name]: e.target.value,
}));
};
const onCheckBoxChange = (event) => {
if (event.target.checked) {
setFormData((prevState) => ({
...prevState,
displayName: prevState.name,
}));
}
}
// handle onChange event of the dropdown
const handleScopes = (e) => {
setScopes(Array.isArray(e) ? e.map((x) => x.value) : []);
};
const handleInstitutionScope = (e) => {
setInstitutionScope(Array.isArray(e) ? e.map((x) => x.value) : []);
};
// const handleEnvironment = (e) => {
// setEnvironment(Array.isArray(e) ? e.map((x) => x.value) : []);
// };
const scopesOptions = [
{
label: "Transactions",
value: "Transactions",
},
{
label: "Accounts",
value: "Accounts",
},
];
const institutionScopeOptions = [
{
label: "Neobanks",
value: "Neobanks",
},
{
label: "DeFi/CeFi",
value: "DeFi/CeFi",
},
{
label: "Personal finance",
value: "Personal finance",
},
{
label: "Investments",
value: "Investments",
},
{
label: "Wallets",
value: "Wallets",
},
];
const dispatch = useDispatch();
useEffect(() => {
if (isError) {
toast({
title: "Error",
description: message,
status: "error",
position: "top-right",
duration: 5000,
isClosable: true,
});
dispatch(reset());
}
if (isAppSuccess) {
toast({
title: "App created",
description: "Refreshing page",
status: "success",
position: "top-right",
duration: 5000,
isClosable: true,
});
dispatch(reset());
onClose();
}
}, [isAppSuccess, reset]);
const onSubmit = async (e) => {
e.preventDefault();
const appData = {
name,
displayName,
product,
// icon,
scopes,
reason,
institutionScope,
environment,
};
dispatch(createApp(appData));
};
function SubmitButton() {
if (
name?.length &&
displayName?.length &&
scopes?.length &&
environment?.length &&
reason?.length > 8 &&
institutionScope?.length > 0
) {
return (
);
} else {
return (
);
}
}
return (
Create an app
);
};
这是应用程序页面:
import {
Box,
Button,
Flex,
Spacer,
Center,
Skeleton,
SkeletonCircle,
SkeletonText,
Text,
VStack,
Image,
Spinner,
SimpleGrid,
HStack,
Avatar,
Stack,
Select,
Hide,
Tag,
} from "@chakra-ui/react";
import React, { useState, useEffect } from "react";
import { MdFilterList } from "react-icons/md";
import { IoIosApps } from "react-icons/io";
import { ArrowLeftIcon, ArrowRightIcon, SpinnerIcon } from "@chakra-ui/icons";
import { CreateAnApp } from "../../../../components/Buttons/CreateAnApp";
import { useDispatch, useSelector } from "react-redux";
import { getAllApps } from "../../../../features/apps/appSlice";
import moment from "moment";
import { Link, useNavigate } from "react-router-dom";
import Card from "#components/Card/Card";
import CardBody from "#components/Card/CardBody";
import transaction_blue from "#assets/svg/transaction_blue.svg";
import { BsPlusCircleFill } from "react-icons/bs";
import useLocalStorage from "use-local-storage";
const Apps = () => {
const dispatch = useDispatch();
const { apps, isLoading, isAppSuccess, meta } = useSelector(
(state) => state.app
);
const [mode] = useLocalStorage("apiEnv", false);
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 2000);
}, [loading]);
const fetchApps = () => {
dispatch(getAllApps());
};
useEffect(() => {
fetchApps();
}, [isAppSuccess]);
return (
<>
}
variant="outline"
textColor="black"
borderRadius="lg"
fontSize={{ sm: "xs", md: "sm" }}
fontWeight="normal"
>
Filter
}
fontWeight="normal"
/>
{isLoading ? (
) : (
{isLoading ? (
) : apps && apps?.length > 0 ? (
apps &&
apps?.map((app) => {
return (
{app.environment === "Sandbox" && (
Sandbox
)}
{app.environment === "Production" && (
Production
)}
{app.product}
{app.displayName}
Created on {moment(app.createdAt).format("LL")}
);
})
) : (
No apps yet
Create an app to get started
}
bg="#002C8A"
_hover={{ bg: "#002C6A" }}
color="white"
/>
)}
{apps && apps?.length > 1 && (
}
bg="#f5f5f5"
textColor="black"
border="2px"
borderColor="gray.400"
border
fontSize={{ sm: "sm", md: "2xl" }}
fontWeight="bold"
p={2}
w={{ sm: "85%", md: "350px" }}
/>
)}
)}
>
);
};
export default Apps;
还有我的 appSlice:
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import appService from "./appService";
const initialState = {
apps: [],
app: [],
isLoading: false,
isAppLoading: false,
isError: false,
isAppSuccess: false,
isSuccess: false,
message: "",
};
// Create new app
export const createApp = createAsyncThunk(
"app/createApp",
async (appData, thunkAPI) => {
try {
const token = sessionStorage.getItem("token");
return await appService.createApp(appData, token);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
// Get all apps
export const getAllApps = createAsyncThunk(
"app/getAllApps",
async (_, thunkAPI) => {
try {
const token = sessionStorage.getItem("token");
return await appService.getAllApps(token);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const appSlice = createSlice({
name: "app",
initialState,
reducers: {
reset: (state) => {
(state.isLoading = false),
(state.isAppSuccess= false),
(state.isAppLoading = false),
(state.isSuccess = false),
(state.isError = false),
(state.message = "");
},
},
extraReducers: (builder) => {
builder
.addCase(createApp.pending, (state) => {
state.isAppLoading = true;
state.isError = false;
})
.addCase(createApp.fulfilled, (state, action) => {
state.isAppLoading = false;
state.isAppSuccess = true;
state.app = action.payload;
})
.addCase(createApp.rejected, (state, action) => {
state.isAppLoading = false;
state.isError = true;
state.message = action.payload;
})
.addCase(getAllApps.pending, (state) => {
state.isLoading = true;
state.isError = false;
})
.addCase(getAllApps.fulfilled, (state, action) => {
state.isLoading = false;
state.apps = action.payload.payload.data;
})
.addCase(getAllApps.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
},
});
export const { reset } = appSlice.actions;
export default appSlice.reducer;
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
我通过将 toast 消息函数从“创建应用程序”中的 useEffect 挂钩移动到“应用程序”页面来修复此问题。刚刚在厕所里想出来的哈哈。我无法对此进行更多阐述,因为我还没有完全理解它。我们每天都在学习
更新了“创建应用程序”按钮中的 useEffect 挂钩:
useEffect(() => { if (isError) { dispatch(reset()); } if (isAppSuccess) { dispatch(reset()); onClose(); } }, [isAppSuccess]);更新的应用程序页面:
useEffect(() => { if (isError) { toast({ title: "Error", description: message, status: "error", position: "top-right", duration: 5000, isClosable: true, }); } if (isAppSuccess) { toast({ title: "App created", description: "Refreshing page", status: "success", position: "top-right", duration: 5000, isClosable: true, }); } }, [isAppSuccess]);