javascript - Spread ოპერატორი არ მუშაობს setState-თან (React, Typescript & Material-ui)
მე მივუშენე კასტომური Snackbar კომპონენტი React-ში Material-ui-ს (და Typescript-ის) გამოყენებით. მე მჭირდებოდა გაგება, როგორ მუშაობს spread ოპერატორი და await. (მაგალითი აქ: https://codesandbox.io/s/gifted-hopper-tkkpi?file=/src/App.tsx:1311-1380)
სრული კოდი:
import React, { FunctionComponent, useEffect } from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import { Color } from "@material-ui/lab";
import { Alert } from "@material-ui/lab";
interface SnackbarModel {
open: boolean;
severity?: Color;
message: string;
}
const CustomSnackbar: FunctionComponent<SnackbarModel> = (props) => {
const [opened, setOpened] = React.useState(false);
useEffect(() => {
if (props.open) {
setOpened(true);
}
}, [props.open]);
const handleClose = (event: React.ChangeEvent<object>, reason: string) => {
if (reason === "clickaway") {
return;
}
setOpened(false);
};
return (
<div>
<Snackbar
open={opened}
autoHideDuration={2000}
onClose={handleClose}
anchorOrigin={{ vertical: "top", horizontal: "center" }}
>
<Alert severity={props.severity}>{props.message}</Alert>
</Snackbar>
</div>
);
};
export default function App() {
const [snackbar, setSnackbar] = React.useState<SnackbarModel>({
open: false,
message: ""
});
async function handleClick() {
await fetch(Stuff...)
// Working as desired
//setSnackbar({open: true, severity: "success", message: "Snackbar"});
//setSnackbar({open: false, severity: "success", message: "Snackbar"});
// Somehow not working
setSnackbar({ open: true, severity: "success", message: "Snackbar" });
setSnackbar({ ...snackbar, open: false });
}
return (
<div>
<CustomSnackbar
open={snackbar.open}
message={snackbar.message}
severity={snackbar.severity}
/>
<Button variant="contained" onClick={handleClick}>
Snackbar
</Button>
</div>
);
}
მე ვაჩქარებ Snackbar-ის გახსნას
const [snackbar, setSnackbar] = React.useState<SnackbarModel>({
open: false,
message: ""
});
async function handleClick() {
await fetch(Stuff)
setSnackbar({open: true, severity: "success", message: "Snackbar"})
setSnackbar({open: false, severity: "success", message: "Snackbar"});
}
Snackbar ავტომატურად იხურება განსაზღვრულ დროში. „open“ მხოლოდ აქტიურდებს Snackbar-ს და არ აკონტროლებს Snackbar-ის setOpen-ს მდგომარეობას.
ახლა ვცადე:
setSnackbar({ open: true, severity: "success", message: "Snackbar" });
console.log(snackbar); //gives you: Object {open: false, message: ""}
setSnackbar({ ...snackbar, open: false });
ეს წარმატებით იწვევს Snackbar-ს, მაგრამ მესიჯი/ტექსტი იკარგება.
შეკითხვა 1: ნუთუ „...snackbar“-ს მაინც არ უნდა ჰქონდეს შენახული „message: 'Snackbar'“?
რამინი მიზეზის გამო, მე ასევე უნდა გავხადო ფუნქცია ასინქრონული და ველოდო პირველ "setSnackbar"-ს, თუ მე არ ვიყენებ "await fetch(Stuff...)"
async function handleClick() {
//working as desired
await setSnackbar({open: true, severity: "success", message: "Snackbar"});
setSnackbar({open: false, severity: "success", message: "Snackbar"});
// OR
//not working as desired
await setSnackbar({ open: true, severity: "success", message: "Snackbar" });
setSnackbar({ ...snackbar, open: false });
}
თუმცა, 'setSnackbar'-ის მოსვლა საჭირო არ ჩანს, თუ გაქვთ სხვა კოდექსი, რომლის დასრულებას ელით. ჩემს შემთხვევაში, ეს ასე მუშაობს:
async function handleClick() {
await fetch(Stuff...)
setSnackbar({open: true, severity: "success", message: "Snackbar"});
setSnackbar({open: false, severity: "success", message: "Snackbar"});
}
კითხვა 2: ვინმე შეგიძლია ახსნას, რატომ არის საჭირო/არ არის საჭირო await?
პასუხი #1
თქვენი იმპლემენტაციის მთავარი პრობლემა არის ის, რომ setSnackbar მოპყრობილია ისე, თითქოს სინქრონული იყოს, მაგრამ ის „ნაწილობრივ“ სინქრონული არაა. აქ სრულად არის აღწერილი რატომ: useState set მეთოდი ცვლილებას არ ასახავს მაშინვე (განსაკუთრებით მეორე პასუხი)
პასუხი 1:
სპრედ ოპერატორი არ მუშაობს, რადგან უმეტეს შემთხვევაში პირველი setSnackbar დასრულებული არ არის.
ამის გამოსასწორებლად, თქვენ უნდა გამოიყენოთ useEffect მეთოდი და გადმოყაროთ snackback ობიექტის open ატრიბუტი როგორც დამოკიდებულება მატრიცაში.
შენ უნდა გააკეთო იგივე, რაც გააკეთე შენს CustomSnackbar-ში:
useEffect(() => {
if (snackbar.open) {
setSnackbar((_snackbar) => ({ ..._snackbar, open: false }));
}
}, [snackbar.open]);
შეამჩნიო _snackbar, ის გამოიყენება setSnackbar-ის callback-ში, რომ მიიღოს იმ დროის ღია ატრიბუტის მნიშვნელობა.
პასუხი 2: დაველოდოთ.
თქვენსთვის საჭიროა await API-ზე ბრძანებებისთვის, რადგან API-ის ბრძანებები ასინქრონია/პრომისებია, რაც ნიშნავს, რომ პროგრამა განახორციელებს შემდეგი ბრძანებას მანამდე, სანამ ისინი დასრულდება, ამიტომ თქვენ უნდა "გაცდეთ" მათზე, ან გამოიყენოთ .then() იმისათვის, რომ რაღაც გააკეთოთ, როდესაც ისინი დასრულდება.
setSnackbar ეს არ არის პირობა (promise), მაგრამ მიუხედავად ამისა, ის არ მოქმედებს დაუყოვნებლივ, სწორედ ამიტომ აქვს React-ს setState-სა და useEffect-ისთვის callback-ები. ასეთი შემთხვევების შესასწავლად. ამიტომ, await-ის გამოყენება setSnackbar-ის წინ არ ქმნის განსხვავებას
