Mesej roti bakar muncul dua kali apabila mengimport 2 komponen dalam satu halaman
P粉674999420
P粉674999420 2023-09-14 18:10:15
0
1
641

Saya menghadapi masalah pelik ini dan ini adalah kali pertama saya menghadapinya. Saya mencipta butang yang menggunakan kit alat Redux untuk mengendalikan penciptaan aplikasi. Mengikut reka bentuk UI, butang harus muncul dua kali pada halaman seperti yang ditunjukkan di bawah. Butang yang diserlahkan adalah komponen yang sama.

Jika saya cuba mencipta apl, ia memaparkan dua mesej roti bakar:

Saya perasan bahawa jika saya mengalih keluar salah satu butang "Buat Apl" dan menyimpannya, maka saya cuba mencipta apl ia hanya memaparkan mesej Toast, seperti yang dijangkakan.

Adakah amalan terbaik yang ideal untuk mencipta 2 butang berasingan untuk mengendalikan satu fungsi?

Ini ialah butang 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) => (
    <img
      key={file.name}
      src={file.preview}
      alt="image"
      style={{ width: "50%", height: "50%" }}
    />
  ));

  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 (
        <Button
          fontSize={{ sm: "12px", md: "14px" }}
          type="submit"
          borderRadius="md"
          color="white"
          bg="#002C8A"
          _hover={{ bg: "#002C6A" }}
          width={{ sm: "300px", md: "400px" }}
        >
          {isAppLoading ? <Spinner /> : "Create app"}
        </Button>
      );
    } else {
      return (
        <Button
          fontSize={{ sm: "12px", md: "14px" }}
          type="submit"
          borderRadius="md"
          color="white"
          bg="#002C8A"
          _hover={{ bg: "#002C6A" }}
          width={{ sm: "300px", md: "400px" }}
          isDisabled
        >
          {isAppLoading ? <Spinner /> : "Create app"}
        </Button>
      );
    }
  }

  return (
    <div>
      <Button
        {...rest}
        leftIcon={leftIcon}
        onClick={onOpen}
        bg={bg}
        textColor={textColor}
        borderRadius="lg"
        variant="solid"
        fontSize={fontSize}
        _hover={_hover}
        fontWeight={fontWeight}
      >
        Create an app
      </Button>

      <Modal
        size="lg"
        closeOnOverlayClick={false}
        isOpen={isOpen}
        onClose={onClose}
      >
        <ModalOverlay />
        <ModalContent mt={1}>
          <ModalHeader textAlign="center" fontSize="md" color="#002c8a">
            Create an app
          </ModalHeader>
          <ModalCloseButton />
          <form onSubmit={onSubmit}>
            <ModalBody pb={6}>
              <Flex flexDirection={{ sm: "column", md: "row" }}>
                <Box>
                  <Box mb={6}>
                    <FormControl>
                      <FormLabel fontSize="sm" fontWeight="semibold">
                        Add a logo to personalize your app
                      </FormLabel>
                      <Box
                        width={{ sm: "340px", md: "450px" }}
                        className="dropArea"
                        {...getRootProps()}
                      >
                        <input {...getInputProps()} />
                        <Flex
                          className="text"
                          width={{ sm: "340px", md: "450px" }}
                        >
                          {images?.length > 0 && (
                            <>
                              <div>{images}</div>
                            </>
                          )}
                          {images?.length === 0 && (
                            <>
                              <Box>
                                <AiOutlineCloudUpload size={30} />
                              </Box>
                              <Box>
                                <Text fontSize="sm">
                                  Drop app icon here or{" "}
                                  <Button
                                    variant="link"
                                    fontSize="sm"
                                    color="#002c8a"
                                  >
                                    browse
                                  </Button>
                                </Text>
                              </Box>
                            </>
                          )}
                        </Flex>
                      </Box>
                    </FormControl>
                  </Box>
                  <Flex mt={6}>
                    <Box>
                      <FormControl>
                        <FormLabel fontSize="sm" fontWeight="semibold">
                          App Name
                        </FormLabel>
                        <Input
                          fontSize="14"
                          width={{ sm: "165px", md: "225px" }}
                          name="name"
                          type="name"
                          value={name}
                          onChange={onChange}
                        />
                      </FormControl>
                      <Checkbox mt={1} onChange={onCheckBoxChange} size='sm'  css={`
                      > span:first-of-type {
                        box-shadow: unset;
                      }
                    `}><Text fontSize="10.9px">Use as display name</Text></Checkbox>
                    </Box>
                    <Box ml={2}>
                      <FormControl>
                        <FormLabel fontSize="sm" fontWeight="semibold">
                          Display Name
                        </FormLabel>
                        <Input
                          fontSize="14"
                          width={{ sm: "165px", md: "225px" }}
                          name="displayName"
                          type="name"
                          value={displayName}
                          onChange={onChange}
                        />
                      </FormControl>
                    </Box>
                  </Flex>
                  <Box mt={3}>
                    <FormLabel fontSize="sm" fontWeight="semibold">
                      Product
                    </FormLabel>
                    <Select
                      name="product"
                      placeholder=" "
                      fontSize="14"
                      value={product}
                      onChange={onChange}
                    >
                      <option value="Connect">Connect</option>
                      <option value="Directpay">Directpay</option>
                    </Select>
                  </Box>
                  <Box w="100%" mt={3}>
                    <FormLabel fontSize="sm" fontWeight="semibold">
                      Account
                    </FormLabel>
                    <Select1
                      useBasicStyles
                      isMulti
                      name="scopes"
                      colorScheme="blue"
                      placeholder=" "
                      options={scopesOptions}
                      closeMenuOnSelect={false}
                      value={scopesOptions?.filter((obj) =>
                        scopes?.includes(obj.value)
                      )} // set selected values
                      onChange={handleScopes}
                    />
                  </Box>
                  <Box w="100%" mt={3}>
                    <FormLabel fontSize="sm" fontWeight="semibold">
                      Institution
                    </FormLabel>
                    <Select1
                      useBasicStyles
                      isMulti
                      name="institution"
                      colorScheme="blue"
                      placeholder=" "
                      _placeholder={{ color: "red" }}
                      options={institutionScopeOptions}
                      closeMenuOnSelect={false}
                      value={institutionScopeOptions?.filter((obj) =>
                        institutionScope?.includes(obj.value)
                      )} // set selected values
                      onChange={handleInstitutionScope}
                    />
                  </Box>
                  <Box w="100%" mt={3}>
                  <FormLabel fontSize="sm" fontWeight="semibold">
                      Environment
                    </FormLabel>
                    <Select
                      name="environment"
                      placeholder=" "
                      fontSize="14"
                      color="black"
                      value={environment}
                      onChange={onChange}
                    >
                      <option value="Sandbox">Sandbox</option>
                      <option value="Production">Production</option></Select>
                    {/*<FormLabel fontSize="sm" fontWeight="semibold">
                      Environment
                    </FormLabel>
                    <Select1
                      useBasicStyles
                      name="environment"
                      isMulti
                      placeholder=" "
                      options={environmentOptions}
                      closeMenuOnSelect={true}
                      color="black"
                      value={environmentOptions?.filter((obj) =>
                        environment?.includes(obj.value)
                      )} // set selected values
                      onChange={handleEnvironment}
                      />*/}
                  </Box>

                  <Box w="100%" mt={4}>
                    <Textarea
                      placeholder="Reason for data access"
                      fontSize="sm"
                      value={reason}
                      name="reason"
                      type="string"
                      onChange={onChange}
                      colorScheme="blue"
                    />
                  </Box>
                </Box>
              </Flex>
            </ModalBody>

            <Wrap mb={6} justify="center">
              <SubmitButton />
            </Wrap>
          </form>
        </ModalContent>
      </Modal>
    </div>
  );
};

Ini adalah halaman permohonan:

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 (
    <>
        <Flex alignItems="center" mt={-3} ml={-4} p="5px" mb="10px">
          <Spacer />
          <Flex>
            <Box>
              <Skeleton borderRadius="lg" isLoaded={!loading}>
                <Button
                  leftIcon={<MdFilterList size={20} />}
                  variant="outline"
                  textColor="black"
                  borderRadius="lg"
                  fontSize={{ sm: "xs", md: "sm" }}
                  fontWeight="normal"
                >
                  Filter
                </Button>
              </Skeleton>
            </Box>
            <Box ml={4}>
              <Skeleton borderRadius="lg" isLoaded={!loading}>
                <CreateAnApp
                  bg="#002C8A"
                  textColor="white"
                  fontSize={{ sm: "xs", md: "sm" }}
                  _hover={{ bg: "#002C6A" }}
                  leftIcon={<BsPlusCircleFill size={16} />}
                  fontWeight="normal"
                />
              </Skeleton>
            </Box>
          </Flex>
        </Flex>
        {isLoading ? (
          <Center>
            <Spinner mt={20} />
          </Center>
        ) : (
          <SimpleGrid mt={10} minChildWidth="360px" spacing="40px">
            {isLoading ? (
              <Center>
                <Spinner mt={20} />
              </Center>
            ) : apps && apps?.length > 0 ? (
              apps &&
              apps?.map((app) => {
                return (
                  <Skeleton borderRadius="lg" isLoaded={!loading}>
                    <Box
                      _hover={{ bg: "white" }}
                      h="150px"
                      as="button"
                      shadow="lg"
                      p={2}
                      w={{ sm: "85%", md: "350px" }}
                      bg="#f5f5f5"
                      borderRadius="lg"
                    >
                      <Link to={`/admin/viewapp/${app.uid}`}>
                        <Box>
                          {app.environment === "Sandbox" && (
                            <Box align="right" mt={-4}>
                              <Tag
                                variant="solid"
                                borderRadius="10px"
                                size="sm"
                                colorScheme="orange"
                                fontSize="xs"
                                textTransform="uppercase"
                              >
                                Sandbox
                              </Tag>
                            </Box>
                          )}

                          {app.environment === "Production" && (
                            <Box align="right" mt={-4}>
                              <Tag
                                variant="solid"
                                borderRadius="10px"
                                colorScheme="green"
                                size="sm"
                                fontSize="xs"
                                textTransform="uppercase"
                              >
                                Production
                              </Tag>
                            </Box>
                          )}
                          <HStack>
                            <Box mt={3} ml={6}>
                              <Avatar
                                bg="black"
                                color="white"
                                name={app.name}
                              />
                            </Box>
                            <Box>
                              <Stack ml={2}>
                                <Box mt={-1}>
                                  <Text
                                    color="orange"
                                    textTransform="uppercase"
                                    fontSize="12px"
                                  >
                                    {app.product}
                                  </Text>
                                </Box>
                                <Box>
                                  <Text
                                    fontSize={{ sm: "sm", md: "lg" }}
                                    fontWeight="bold"
                                  >
                                    <SkeletonText isLoaded={!loading}>
                                      {app.displayName}
                                    </SkeletonText>
                                  </Text>
                                </Box>
                              </Stack>
                            </Box>
                          </HStack>
                          <Text fontSize="sm">
                            Created on {moment(app.createdAt).format("LL")}
                          </Text>
                        </Box>
                      </Link>
                    </Box>
                  </Skeleton>
                );
              })
            ) : (
              <Center ml={{ sm: "0", md: -32 }}>
                <VStack spacing={4} align="stretch">
                  <Box>
                    <Center>
                      <SkeletonCircle isLoaded={!loading}>
                        <IoIosApps size={45} />
                      </SkeletonCircle>
                    </Center>
                  </Box>
                  <Box>
                    <Text fontSize="30px" fontWeight={700}>
                      <SkeletonText isLoaded={!loading}>
                        No apps yet
                      </SkeletonText>
                    </Text>
                    <Text mb={4}>
                      <SkeletonText noOfLines={1} mt={2} isLoaded={!loading}>
                        Create an app to get started
                      </SkeletonText>
                    </Text>
                    <Skeleton borderRadius="lg" isLoaded={!loading}>
                      <CreateAnApp
                        w="200px"
                        h="50px"
                        leftIcon={
                          <BsPlusCircleFill
                            className="bg-[#002C8A] hover: none text-white"
                            size={30}
                          />
                        }
                        bg="#002C8A"
                        _hover={{ bg: "#002C6A" }}
                        color="white"
                      />
                    </Skeleton>
                  </Box>
                  <Box></Box>
                </VStack>
              </Center>
            )}

            {apps && apps?.length > 1 && (
              <Skeleton borderRadius="lg" isLoaded={!loading}>
                <CreateAnApp
                  h="150px"
                  ml={{ sm: 0, md: -20 }}
                  shadow="lg"
                  leftIcon={
                    <BsPlusCircleFill
                      className="bg-[#f5f5f5] text-blue-800"
                      size={30}
                    />
                  }
                  bg="#f5f5f5"
                  textColor="black"
                  border="2px"
                  borderColor="gray.400"
                  borderStyle="dashed"
                  fontSize={{ sm: "sm", md: "2xl" }}
                  fontWeight="bold"
                  p={2}
                  w={{ sm: "85%", md: "350px" }}
                />
              </Skeleton>
            )}
          </SimpleGrid>
        )}
      </Box>
    </>
  );
};

export default Apps;

Dan appSlice saya:

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;

P粉674999420
P粉674999420

membalas semua(1)
P粉282627613

Saya membetulkannya dengan mengalihkan fungsi mesej roti bakar daripada cangkuk useEffect dalam Cipta Apl ke halaman Apl. Saya baru tahu di bilik air haha. Saya tidak boleh menghuraikan lebih lanjut mengenai perkara ini kerana saya belum faham sepenuhnya. Kami belajar setiap hari

Terkemas kini cangkuk useEffect dalam butang "Buat Apl":

useEffect(() => {

    if (isError) {
      dispatch(reset());
    }

    if (isAppSuccess) {
      dispatch(reset());
      onClose();
    }
  }, [isAppSuccess]);

Halaman apl yang dikemas kini:

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]);
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan