ΠΠΎΠ»ΡΡΠΈΠ½ΡΡΠ²ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ ΡΠ΅Π³ΠΎΠ΄Π½Ρ ΠΌΠ΅Π½ΡΡΡΡΡ ΠΠΎΡΡΠ΄Π°ΡΡΡΠ²ΠΎ ΠΎΡΠ½ΠΎΠ²Π°Π½Π½ΡΠΉ Π½Π° Π½Π°ΠΌΠ΅ΡΠ΅Π½ΠΈΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΡΡΡ Π±ΠΎΠ»Π΅Π΅ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΌ, Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΡΠΌΠΈ Π΄Π»Ρ ΡΡΠΊ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ΡΡ Π½Π° ΠΊΠ»ΠΈΠΊΠΈ, ΠΊΡΠ°Π½Ρ, ΠΏΡΠ΅ΡΡΡ ΠΈ Ρ. Π. Π’Π΅ΠΌ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅, ΠΊΠ°ΠΆΠ΄ΡΠΉ Π½Π°ΠΌΠ΅ΡΠ΅Π½ Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ Π² Π½Π°ΡΠΈΡ ΠΌΠΎΠ·Π³Π°Ρ .
Π‘Π΅Π³ΠΎΠ΄Π½Ρ ΠΌΡ ΡΠΎΠ±ΠΈΡΠ°Π΅ΠΌΡΡ ΠΏΠΎΡΡΡΠΎΠΈΡΡ Π΄ΡΡΠ³ΠΎΠΉ ΡΠΈΠΏ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ. ΠΡ ΠΏΠΎΡΡΡΠΎΠΈΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΠ΅Π½ΡΠ΅Ρ ΠΠΎΡΡΠ΄Π°ΡΡΡΠ²ΠΎ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ Π²Π°ΡΠ΅Π³ΠΎ ΠΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΡΠΊΠ°Π·
ΠΡΡΠ»ΡΡΠ°ΠΉ ΠΌΠ΅Π½Ρ.
Π§ΡΠΎ Π΅ΡΠ»ΠΈ Π½Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»ΠΎ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ WebGL ΠΎΠΊΠ΅Π°Π½ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ Π²Π°ΡΠ΅Π³ΠΎ Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ ΡΡΠΎΠ²Π΅Π½Ρ? ΠΠΏΡΡ Β«Π²ΠΈΠ·ΡΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈΒ», ΠΊΠΎΡΠΎΡΡΠΌ Π²Ρ ΡΡΠ²ΡΡΠ²ΡΠ΅ΡΠ΅ ΡΠ΅Π±Ρ.
ΠΠ΅ΡΠ²ΡΠΉ ΡΠ°Π³ Π±ΡΠ΄Π΅Ρ ΠΈΠ·ΠΌΠ΅ΡΡΡΡ ΠΈ ΠΏΠΎΠ»ΡΡΠ°ΡΡ Π΄ΠΎΡΡΡΠΏ ΠΊ ΡΠ°ΠΊΠΈΠΌ Π΄Π°Π½Π½ΡΠΌ. Π Π΄Π»Ρ ΡΡΠΎΠ³ΠΎ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π³Π°ΡΠ½ΠΈΡΡΡΡ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΡ.
ΠΠ°ΡΠΈΠ½Π°Ρ
ΠΠ°ΡΠ½Π΅ΠΌ Ρ Π·Π°Π³ΡΡΠ·ΠΊΠΈ Π½Π°ΡΠ΅Π³ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΠ΅ΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ RACT (CRA). ΠΡΠΊΡΡΠ²Π°Π΅ΠΌ ΠΏΡΠΎΠ΅ΠΊΡ Π² VS-ΠΊΠΎΠ΄Π΅ ΠΈ Π·Π°ΠΏΡΡΡΠΈΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎ.
NPX Create-racted-appΠΠΎΠ΄-ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΡΠ΅ΠΌΡΠΉ ΡΠΌΠΎΠΌ-ΠΎΠΊΠ΅Π°Π½NPM Start.
ΠΡΠ»ΠΈ Π²ΡΠ΅ ΠΏΠΎΠΉΠ΄Π΅Ρ Ρ ΠΎΡΠΎΡΠΎ, Π²Ρ Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΠ²ΠΈΠ΄Π΅ΡΡ ΡΡΠΎ-ΡΠΎ Π²ΡΠΎΠ΄Π΅ ΡΡΠΎΠ³ΠΎ:
Π‘ΠΎΠ·Π΄Π°ΡΡ React App View ΠΏΠΎ ΡΠΌΠΎΠ»ΡΠ°Π½ΠΈΡ
π Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ
ΠΡ Π²Π΅ΡΠΈΠΌ Π² ΠΊΠΎΠ½ΡΠΈΠ΄Π΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎΡΡΡ. ΠΠΎΡ ΠΏΠΎΡΠ΅ΠΌΡ ΠΏΠΎΠ½ΡΡΠΈΠ΅ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΏΠ΅ΡΠ²ΡΠΌ ΠΌΠΎΠ·Π³ΠΎΠ²ΡΠΌ ΠΊΠΎΠΌΠΏΡΡΡΠ΅ΡΠΎΠΌ Π΄Π»Ρ ΡΡΠ½ΠΊΡΠΈΠΈ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ. ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Auth Π² ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΏΡΠΎΡΡΠΎ. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π½Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ ΡΠΎΡΠΌΠ° Π²Ρ ΠΎΠ΄Π° ΠΈ 3 ΠΏΠΎΠ±ΠΎΡΠ½ΡΠ΅ ΡΡΡΠ΅ΠΊΡΡ Π΄Π»Ρ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ.
ΠΡΠ΅, ΡΡΠΎ Π²Π°ΠΌ Π½ΡΠΆΠ½ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΡΡΡ ΠΊ Π²Π°ΡΠ΅ΠΌΡ ΠΏΠΎΠ½ΡΡΠΈΡ ΠΌΠΎΠ·Π³ΠΎΠ²ΠΎΠΉ ΠΊΠΎΠΌΠΏΡΡΡΠ΅Ρ, ΡΡΠΎ ΠΠ΅Π²ΡΠ°ΡΡΠΎΠ²ΠΎΡΡΡ ΡΡΠ΅ΡΠ° ΠΈ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΡΡΡΡΠΎΠΉΡΡΠ²Π°. ΠΡΠ°ΠΊ, Π΄Π°Π²Π°ΠΉΡΠ΅ Π½Π°ΡΠ½Π΅ΠΌ Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° Π΄Π»Ρ ΡΠΎΡΠΌΡ Π²Ρ ΠΎΠ΄Π° Π² ΡΠΈΡΡΠ΅ΠΌΡ, ΠΊΠΎΡΠΎΡΠ°Ρ Π±ΡΠ΄Π΅Ρ ΡΠΎΠ±ΠΈΡΠ°ΡΡ ΡΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ.
// src/components/LoginForm.js
import React, { useState } from "react";
export function LoginForm({ onLogin, loading, error }) {
const [deviceId, setDeviceId] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
function onSubmit(event) {
event.preventDefault();
onLogin({ deviceId, email, password });
}
return (
);
}
π ΠΠΎΠ±Π°Π²ΠΈΡΡ ΡΡΠΈΠ»ΠΈ Π€ΠΎΡΠΌΠ° – ΠΠΎΡ CSS, ΠΊΠΎΡΠΎΡΡΠ΅ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π» ΠΡΠΊΠ°Π·
ΠΡΠΎΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ ΠΏΡΠΎΠ²Π΅Π΄Π΅Ρ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ deviceid , ΡΠ»Π΅ΠΊΡΡΠΎΠ½Π½ΠΎΠ΅ ΠΏΠΈΡΡΠΌΠΎ ΠΈ ΠΏΠ°ΡΠΎΠ»Ρ ΠΡΠΊΠ°Π· ΠΡΠΎΠΌΠ΅ ΡΠΎΠ³ΠΎ, Π½Π°ΡΠ° ΡΠΎΡΠΌΠ° ΡΠΎΡΠΌΡ ΠΏΡΠΈΠΌΠ΅Ρ ΠΠ½Π»ΠΎΠ³ΠΈΠ½ ΠΎΠΏΠΎΡΡ, ΠΊΠΎΡΠΎΡΡΠΉ Π²ΡΠΏΠΎΠ»Π½ΠΈΡ, ΠΊΠΎΠ³Π΄Π° ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π½Π°ΠΆΠΈΠΌΠ°Π΅Ρ Π½Π° ΠΊΠ½ΠΎΠΏΠΊΡ Β«ΠΡ
ΠΎΠ΄Β». ΠΡ ΡΠ°ΠΊΠΆΠ΅ ΠΏΡΠΈΠΌΠ΅ΠΌ ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΠΎΠΏΠΎΡΡ, ΠΊΠΎΠ³Π΄Π° ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΠ΅ΡΡΡ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠΎΡΠΌΡ, Π° ΠΡΠΈΠ±ΠΊΠ° . ΠΠΏΠΎΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°ΡΡΡΡ ΠΏΡΠΈ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΈ ΠΎΡΠΈΠ±ΠΊΠΈ.
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° ΠΌΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ Π½Π°Ρ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Π²Ρ ΠΎΠ΄Π° Π² ΡΠΈΡΡΠ΅ΠΌΡ, Π΄Π°Π²Π°ΠΉΡΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΡΡΡΠ°Π½ΠΈΡΡ Π²Ρ ΠΎΠ΄Π° Π² ΡΠΈΡΡΠ΅ΠΌΡ, ΠΊΠΎΡΠΎΡΠ°Ρ Π±ΡΠ΄Π΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π½Π°Ρ Π½ΠΎΠ²ΡΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ.
// src/pages/Login.js
import React, { useState, useEffect } from "react";
import { LoginForm } from "../components/LoginForm";
export function Login({ notion, user, setUser, setDeviceId }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [isLoggingIn, setIsLoggingIn] = useState(false);
function onLogin({ email, password, deviceId }) {
if (email && password && deviceId) {
setError("");
setEmail(email);
setPassword(password);
setDeviceId(deviceId);
} else {
setError("Please fill the form");
}
}
return (
);
}
ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Form Π²Ρ ΠΎΠ΄Π°
Π¦Π΅Π»ΡΡ ΡΡΠΎΠΉ ΡΡΡΠ°Π½ΠΈΡΡ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΡΠΎΡΠΌΡ Π²Ρ
ΠΎΠ΄Π° Π² ΡΠΈΡΡΠ΅ΠΌΡ, Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π±Π°Π·ΠΎΠ²ΡΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΡ ΡΠΎΡΠΌΡ ΡΠ΅ΡΠ΅Π· SetError Π€ΡΠ½ΠΊΡΠΈΡ ΠΈ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ ΡΡΠ½ΠΊΡΠΈΡ Π²Ρ
ΠΎΠ΄Π° Π² ΡΠΈΡΡΠ΅ΠΌΡ. ΠΠ»Ρ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π³ΠΎ, Π΄Π°Π²Π°ΠΉΡΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅ΡΡΡ Ρ ΡΠ»Π΅ΠΊΡΡΠΎΠ½Π½ΠΎΠ΅ ΠΏΠΈΡΡΠΌΠΎ , ΠΏΠ°ΡΠΎΠ»Ρ Π ΡΠ΅ΠΊΠ²ΠΈΠ·ΠΈΡΡ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ ΡΡΡΠ°Π½ΠΈΡΡ.
useEffect(() => {
if (!user && notion && email && password) {
login();
}
async function login() {
setIsLoggingIn(true);
const auth = await notion
.login({ email, password })
.catch(error => {
setError(error.message);
});
if (auth) {
setUser(auth.user);
}
setIsLoggingIn(false);
}
}, [email, password, notion, user, setUser, setError]);
ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄ΡΠΌΠ°ΡΡ ΠΎ ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΠ°ΠΊ ΠΎΠ±ΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΡΠ΅Π°Π½Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ AUTH AUTH, ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½ΡΠΉ API ΠΏΠΎΠ½ΡΡΠΈΡ. ΠΠΎΡΡΠΎΠΌΡ ΠΌΡ Π½Π°Π·ΡΠ²Π°Π΅ΠΌ ΡΠΎΠ»ΡΠΊΠΎ Π½Π°ΡΠΈΠΌΠΈ ΠΠΎΠΉΡΠΈ () Π€ΡΠ½ΠΊΡΠΈΡ, Π΅ΡΠ»ΠΈ Π½Π΅Ρ ΡΠ΅Π°Π½ΡΠ° Π°ΡΡΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ, Ρ Π½Π°Ρ Π΅ΡΡΡ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΏΠΎΠ½ΡΡΠΈΡ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ, ΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΎΡΠΏΡΠ°Π²ΠΈΠ» ΡΠ»Π΅ΠΊΡΡΠΎΠ½Π½ΠΎΠ΅ ΠΏΠΈΡΡΠΌΠΎ ΠΈ ΠΏΠ°ΡΠΎΠ»Ρ.
ΠΡΠ΅Π½Ρ ΡΠΊΠΎΡΠΎ Π²Ρ ΡΠ·Π½Π°Π΅ΡΠ΅, ΠΊΠ°ΠΊ ΠΌΡ ΠΏΠΎΠ»ΡΡΠΈΠΌ ΡΠ΅ΠΊΠ²ΠΈΠ·ΠΈΡΡ: ΠΠΎΠ½ΡΡΠΈΠ΅, ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ, setuser, setdeviceId ΠΡΠΊΠ°Π· ΠΠΎ ΠΏΡΠ΅ΠΆΠ΄Π΅ ΡΠ΅ΠΌ ΠΌΡ ΡΠ΄Π΅Π»Π°Π΅ΠΌ ΡΡΠΎ, Π΄Π°Π²Π°ΠΉΡΠ΅ Π²Π΅ΡΠ½Π΅ΠΌΡΡ ΠΊ Π½Π°ΡΠ΅ΠΌΡ App.js ΠΈ Π½Π°ΡΠ½ΠΈΡΠ΅ ΠΏΠΎΠΌΠ΅ΡΠ°ΡΡ Π²ΡΠ΅ ΡΡΠΎ Π²ΠΌΠ΅ΡΡΠ΅.
βοΈ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ
Π§ΡΠΎΠ±Ρ ΡΠΎΡ
ΡΠ°Π½ΠΈΡΡ ΡΡΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΡΠΎ, ΠΌΡ ΠΏΡΠΎΡΡΠΎ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Regive Π£ΠΌΠ΅ΡΡΠΈΡΠ΅ ΠΡΡΠΊ, ΡΠΎΡΡΠ΅Ρ rain, ΠΈ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΊΡΡΠΊ Π΄Π»Ρ Ρ
ΡΠ°Π½Π΅Π½ΠΈΡ, ΠΏΡΠΈΠ²Π»Π΅ΡΠ΅Π½Π½ΡΠΉ Π²Π°ΠΌ ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ π π ΠΡΠΊΠ°Π· ΠΡΠΎ ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ, ΡΡΠΎ Π½Π°ΡΠ° ΠΎΠ±ΡΠ°Ρ ΡΡΡΠ°ΡΠ΅Π³ΠΈΡ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Π±ΡΠ΄Π΅Ρ ΡΠΎΡΡΠΎΡΡΡ ΠΈΠ· ΡΠΎΡ
ΡΠ°Π½Π΅Π½ΠΈΡ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ Π½Π° ΡΡΠΎΠ²Π½Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠΎΠ² ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°Π²Π°ΡΡ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΡΠ΅ ΡΠ΅ΠΊΠ²ΠΈΠ·ΠΈΡΡ Π΅Π³ΠΎ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°ΠΌ.
NPM Π£ΡΡΠ°Π½ΠΎΠ²ΠΊΠ° @ Reach/Router Reaction-race
ΠΡ Π½Π°ΡΠ½Π΅ΠΌ Ρ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΌΠ°ΡΡΡΡΡΠ°, Π½ΠΎ ΠΌΡ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π΅ΡΠ΅ 2 ΠΌΠ°ΡΡΡΡΡΠ°, ΡΠ°ΠΊ ΠΊΠ°ΠΊ ΠΌΡ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ ΡΡΡΠΎΠΈΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅.
// src/App.js
import React, { useState, useEffect } from "react";
import { Router, navigate } from "@reach/router";
import useLocalStorage from "react-use/lib/useLocalStorage";
import { Login } from "./pages/Login";
export function App() {
const [notion, setNotion] = useState(null);
const [user, setUser] = useState(null);
const [deviceId, setDeviceId] = useLocalStorage("deviceId");
const [loading, setLoading] = useState(true);
return (
);
}
π Π― Π½Π°ΡΠ΅Π» ΠΠΎΡΡΠΈΡΡ ΠΌΠ°ΡΡΡΡΡΠΈΠ·Π°ΡΠΎΡΠ° Π Π°ΠΉΠ°Π½ΠΎΠΌ Π€Π»ΠΎΡΠ΅Π½ΡΠΈΡ (ΠΈ Π΄ΡΡΠ·ΡΡΠΌΠΈ) Π±ΡΡΡ ΠΈΠ΄Π΅Π°Π»ΡΠ½ΡΠΌ Π±Π°Π»Π°Π½ΡΠΎΠΌ ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΡΠΎΡΡΠΎΡΠΎΠΉ ΠΈ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·ΡΠ΅ΠΌΠΎΡΡΡΡ. ΠΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ ΡΡΠ΅Π·Π²ΡΡΠ°ΠΉΠ½ΠΎ ΡΡΠ½Π° ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΠ»Π° ΠΌΠ½Π΅ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ Π±Π°Π·ΠΎΠ²ΡΡ ΠΌΠ°ΡΡΡΡΡΡ Π² ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΠΌΠΈΠ½ΡΡ.
ΠΡΠ»ΠΈ Π²Π°ΠΌ Π±ΡΠ»ΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ, ΠΏΠΎΡΠ΅ΠΌΡ ΠΌΡ ΡΠ΅ΡΠΈΠ»ΠΈ ΡΠΎΡ
ΡΠ°Π½ΠΈΡΡ deviceid Π ΠΌΠ΅ΡΡΠ½ΠΎΠΌ Ρ
ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ ΡΡΠΎ ΠΏΠΎΡΠΎΠΌΡ, ΡΡΠΎ Π½Π°ΠΌ Π½ΡΠΆΠ½ΠΎ Π±ΡΠ΄Π΅Ρ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΄ΠΎΡΡΡΠΏ ΠΊ Π½Π΅ΠΌΡ Π΄ΠΎΠ±ΡΠ°ΡΡΡΡ Π΄ΠΎ ΠΈ ΠΏΠΎΡΠ»Π΅ ΡΠΎΠ³ΠΎ, ΠΊΠ°ΠΊ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π²ΠΎΠΉΠ΄Π΅Ρ Π² ΡΠΈΡΡΠ΅ΠΌΡ. ΠΡΠΎ ΡΠ°ΠΊΠΆΠ΅ Π΄Π΅Π»Π°Π΅Ρ Π±ΠΎΠ»Π΅Π΅ ΠΏΡΠΈΡΡΠ½ΡΠΉ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΎΠΏΡΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Π²ΠΎΠ΄ΠΈΡΡ Π΅Π³ΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ°Π·.
π§ ΠΠΎΠ½ΡΡΠΈΠ΅
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Ρ Π½Π°Ρ Π΅ΡΡΡ Π±Π°Π·ΠΎΠ²ΠΎΠ΅ Π³ΠΎΡΡΠ΄Π°ΡΡΡΠ²Π΅Π½Π½ΠΎΠ΅ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° ΠΌΠ΅ΡΡΠ΅, Π΄Π°Π²Π°ΠΉΡΠ΅ ΠΈΠ½ΡΠ΅Π³ΡΠΈΡΡΠ΅ΠΌ Π½Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Ρ ΠΠΎΠ½ΡΡΠΈΠ΅ Π£ΡΡΠ°Π½ΠΎΠ²ΠΈΠ² API ΠΈ ΠΈΠΌΠΏΠΎΡΡΠΈΡΡΡ Π΅Π³ΠΎ Π² App.js ΠΡΠΊΠ°Π·
NPM ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ @neurosity/ΠΏΠΎΠ½ΡΡΠΈΠ΅
π€― API ΠΏΠΎΠ½ΡΡΠΈΡ ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΠ²Π°Π΅Ρ ΠΏΠΎΠ»Π½ΡΡ ΡΠ²ΡΠ·Ρ ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΡΡΠΎΠΉΡΡΠ²ΠΎΠΌ ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ΠΌ. ΠΡ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΅Π³ΠΎ, ΡΡΠΎΠ±Ρ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΠΎΠ±ΡΠ°ΡΠ½ΡΡ ΡΠ²ΡΠ·Ρ Π² ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΠΊΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΌΡ Π±ΡΠ΄Π΅ΠΌ ΡΠ°Π±ΠΎΡΠ°ΡΡ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΠΎ Ρ Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ API.
import { Notion } from "@neurosity/notion";
ΠΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΠΊ ΡΡΡΡΠΎΠΉΡΡΠ²Ρ ΠΏΠΎΠ½ΡΡΠΈΡ ΠΏΡΠΎΡΡ. ΠΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ Π½ΠΎΠ²ΡΡ ΠΠΎΠ½ΡΡΠΈΠ΅ ΠΈ ΠΏΡΠΎΠΏΡΡΡΠΈΡΠ΅ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ ΡΡΡΡΠΎΠΉΡΡΠ²Π°. ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅Ρ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ ΠΏΡΡΠ΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ deviceid ΠΡΠΊΠ°Π·
π ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π½Π°ΠΉΡΠΈ ΠΏΠΎΠ»Π½ΡΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ ΠΏΠΎ ΠΏΠΎΠ½ΡΡΠΈΡ Π½Π° docs.neurosity.co ΠΡΠΊΠ°Π·
useEffect(() => {
if (deviceId) {
const notion = new Notion({ deviceId }); // π²
setNotion(notion);
} else {
setLoading(false);
}
}, [deviceId]);
ΠΡΡΠ³ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅, ΠΊΠΎΡΠΎΡΠΎΠ΅ ΠΌΡ Ρ
ΠΎΡΠΈΠΌ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ, ΡΡΠΎ ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π³ΠΎΡΡΠ΄Π°ΡΡΡΠ²ΠΎ.
Π ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅ ΠΌΡ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅ΡΡΡ ΡΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ΠΌ ΠΠΎΠ½ΡΡΠΈΠ΅ ΠΏΡΠΈΠΌΠ΅Ρ. ΠΡΠ»ΠΈ ΠΠΎΠ½ΡΡΠΈΠ΅ Π΅ΡΠ΅ Π½Π΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½, ΡΠΎ ΠΌΡ ΠΏΡΠΎΠΏΡΡΡΠΈΠΌ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΡ Π½Π° Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ Π‘ΠΎΠ±ΡΡΠΈΡ Π΄ΠΎ ΠΠΎΠ½ΡΡΠΈΠ΅ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΡΠΎΠ·Π΄Π°Π½.
useEffect(() => {
if (!notion) {
return;
}
const subscription = notion.onAuthStateChanged().subscribe(user => {
if (user) {
setUser(user);
} else {
navigate("/");
}
setLoading(false);
});
return () => {
subscription.unsubscribe();
};
}, [notion]);
ΠΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠΌΠ΅Π΅Ρ Π°ΠΊΡΠΈΠ²Π½ΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΡΡ ΡΠ΅Π°Π½Ρ ΡΠΎΡ ΡΠ°Π½ΡΡΡΡΡ ΠΏΠΎ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ ΠΏΠΎΠ½ΡΡΠΈΡ, ΠΌΡ Π·Π°Ρ ΠΎΡΠ΅ΠΌ ΠΏΠΎΠ»ΡΡΠΈΡΡ ΡΠ΅ΠΊΡΡΠΈΠΉ Π²ΠΎΠΉΡΠΈ Π² ΡΠΈΡΡΠ΅ΠΌΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΈ ΡΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ Π΅Π³ΠΎ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π² Π½Π°ΡΠ΅ΠΌ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ.
OnauthstateChanged ΠΠ΅ΡΠΎΠ΄ Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΉ ΠΈΠ· ΡΠΎΠ±ΡΡΠΈΠΉ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. ΠΠ°ΠΆΠ½ΠΎ ΠΎΡΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ API ΠΏΠΎΠ½ΡΡΠΈΡ Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅ ΡΠ΅Π°Π½Ρ ΡΠΎΡ
ΡΠ°Π½ΠΈΡΡΡ ΡΠ΅ΡΠ΅Π· Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠ΅ Ρ
ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅. ΠΡΠ°ΠΊ, Π΅ΡΠ»ΠΈ Π²Ρ Π·Π°ΠΊΡΠΎΠ΅ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ»ΠΈ ΠΏΠ΅ΡΠ΅Π·Π°Π³ΡΡΠ·ΠΈΡΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ, ΡΠ΅Π°Π½Ρ Π±ΡΠ΄Π΅Ρ ΡΠΎΡ
ΡΠ°Π½ΡΡΡΡΡ ΠΈ OnauthstateChanged Π²Π΅ΡΠ½Π΅Ρ ΡΠ΅Π°Π½Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π²ΠΌΠ΅ΡΡΠΎ Π½ΡΠ»Π΅Π²ΠΎΠΉ . ΠΡΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ ΡΠΎ, ΡΡΠΎ ΠΌΡ Ρ
ΠΎΡΠΈΠΌ.
ΠΡΠ»ΠΈ ΡΠ΅Π°Π½Ρ Π½Π΅ ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½, ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠ΅ΡΠ΅ΠΉΡΠΈ Π½Π° ΡΡΡΠ°Π½ΠΈΡΡ Π²Ρ
ΠΎΠ΄Π°. Π ΠΏΡΠΎΡΠΈΠ²Π½ΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Π½Π°Π±ΠΎΡ ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½ΡΠ°.
ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°Π²Π΅ΡΡΠΈΡΡ ΠΏΠΎΠ»Π½ΡΡ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ, Π΄ΠΎΠ±Π°Π²ΠΈΠ² ΡΡΡΠ°Π½ΠΈΡΡ Π²ΡΡ ΠΎΠ΄Π° ΠΈΠ· ΡΠΈΡΡΠ΅ΠΌΡ.
// src/pages/Logout.js
import { useEffect } from "react";
import { navigate } from "@reach/router";
export function Logout({ notion, resetState }) {
useEffect(() => {
if (notion) {
notion.logout().then(() => {
resetState();
navigate("/");
});
}
}, [notion, resetState]);
return null;
}
Π‘ΡΡΠ°Π½ΠΈΡΠ° Β«ΠΡΡ
ΠΎΠ΄Β» – ΡΡΠΎ ΠΏΡΠΎΡΡΠΎ ΡΠ΅Π°ΠΊΡΠΈΠ²Π½ΡΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ Π±Π΅Π· ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² DOM. ΠΠ΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½Π°Ρ Π»ΠΎΠ³ΠΈΠΊΠ°, ΠΊΠΎΡΠΎΡΡΡ Π½Π°ΠΌ Π½ΡΠΆΠ΅Π½, ΡΡΠΎ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΠΎΠ·Π²ΠΎΠ½ΠΈΡ Notion.logout () ΠΠ΅ΡΠΎΠ΄, Π΅ΡΠ»ΠΈ ΠΏΠΎΠ½ΡΡΠΈΠ΅ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΏΡΠΈΡΡΡΡΡΠ²ΡΠ΅Ρ. ΠΠ°ΠΊΠΎΠ½Π΅Ρ, ΠΎΠ½ ΠΏΠ΅ΡΠ΅Π½Π°ΠΏΡΠ°Π²Π»ΡΠ΅Ρ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ Π½Π° Π½Π°ΡΠ°Π»ΡΠ½ΡΠΉ ΠΌΠ°ΡΡΡΡΡ ΠΏΠΎΡΠ»Π΅ Π²ΡΡ
ΠΎΠ΄Π°.
ΠΡΠΎΡ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ ΡΠ΅ΠΏΠ΅ΡΡ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ ΠΊΠ°ΠΊ ΠΌΠ°ΡΡΡΡΡ App.js ΠΡΠΊΠ°Π·
// src/App.js
// ...
import { Logout } from "./pages/Logout";
// ...
return (
{/* ... */}
{
setNotion(null);
setUser(null);
setDeviceId("");
}} />
);
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΈΡ Π·Π°Π²Π΅ΡΡΠ΅Π½Π°, Π΄Π°Π²Π°ΠΉΡΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π»ΠΎΠ³ΠΈΠΊΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ Π½Π°ΡΠ΅Π³ΠΎ ΠΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΠ΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ
π WebGL ΠΎΠΊΠ΅Π°Π½
ΠΠΎΠΌΠ΅Π½Ρ, ΠΊΠΎΠ³Π΄Π° Ρ Π²ΠΈΠ΄Π΅Π» ΠΡΠ²ΠΈΠ΄ WebGL Ocean, Ρ Π²Π»ΡΠ±ΠΈΠ»ΡΡ Π² ΡΡΠΎ. ΠΠΎΡΡΠΎΠΌΡ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΠΏΠΎΠ½ΡΡΠΈΠ΅, ΡΡΠΎΠ±Ρ Π²Π»ΠΈΡΡΡ Π½Π° ΠΏΠΎΠ³ΠΎΠ΄Ρ, ΡΠΏΡΠ°Π²Π»ΡΡΡΡΡ ΠΎΠΊΠ΅Π°Π½ΡΠΊΠΈΠ΅ Π²ΠΎΠ»Π½Ρ, ΠΏΠΎΡΡΠ²ΡΡΠ²ΠΎΠ²Π°Π²ΡΠΈΡΡ Π·Π°Π±Π°Π²Π½ΡΠΌ ΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠΎΠΌ.
π‘ ΠΠ°Π±Π°Π²Π½ΡΠΉ ΡΠ°ΠΊΡ: ΡΡΠΎ ΡΠΈΠΌΡΠ»ΡΡΠΈΡ Ocean Wave Π±ΡΠ»ΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΎ Π² 2013 Π³ΠΎΠ΄Ρ David Li Ρ JavaScript ΠΈ WebGL. ΠΠ½ Π±ΡΠ» ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ ΠΊΠ°ΠΊ ΡΠ°ΡΡΡ ΡΠΊΡΠΏΠ΅ΡΠΈΠΌΠ΅Π½ΡΠΎΠ² Google. Π― Π±ΡΠ» ΡΠ°Π΄ Π²ΠΈΠ΄Π΅ΡΡ, ΡΡΠΎ ΡΡΠΎ Π±ΡΠ»ΠΎ ΠΎΡΠΊΡΡΡΠΎ ΠΏΠΎ Π»ΠΈΡΠ΅Π½Π·ΠΈΠΈ MIT.
ΠΠ»Ρ ΡΡΠΎΠΉ ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΉ ΡΠ°ΡΡΠΈ ΠΈΠ΄Π΅Ρ ΡΠΎΡΡΠΎΠΈΡ Π² ΡΠΎΠΌ, ΡΡΠΎΠ±Ρ ΡΠΎΠ·Π΄Π°ΡΡ Π½ΠΎΠ²ΡΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ, ΠΊΠΎΡΠΎΡΡΠΉ Π±ΡΠ΄Π΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ WebGL Ocean. ΠΡΠ°ΠΊ, Π΄Π°Π²Π°ΠΉΡΠ΅ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΠΊΠ°ΡΠ°Π»ΠΎΠ³, Π½Π°Π·ΡΠ²Π°Π΅ΠΌΡΠΉ Ocean ( ./src/components/ocean ) ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΊ Π½Π΅ΠΌΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠ°ΠΉΠ»Ρ.
- Simulation.js.
- Weather.js.
- Ocean.js:
// src/components/Ocean/Ocean.js
import React, { useState, useEffect, useRef } from "react";
import useRafState from "react-use/lib/useRafState";
import { Simulator, Camera } from "./simulation.js"; // by David Li
import { mapCalmToWeather } from "./weather.js";
const camera = new Camera();
export function Ocean({ calm }) {
const ref = useRef();
const [simulator, setSimulator] = useState();
const [lastTime, setLastTime] = useRafState(Date.now());
useEffect(() => {
const { innerWidth, innerHeight } = window;
const simulator = new Simulator(ref.current, innerWidth, innerHeight);
setSimulator(simulator);
}, [ref, setSimulator]);
useEffect(() => {
if (simulator) {
const currentTime = Date.now();
const deltaTime = (currentTime - lastTime) / 1000 || 0.0;
setLastTime(currentTime);
simulator.render(deltaTime, camera);
}
}, [simulator, lastTime, setLastTime]);
return ;
}
Π Π΅ΡΠ»ΠΈ Π²ΡΠ΅ ΠΏΠΎΠΉΠ΄Π΅Ρ Ρ ΠΎΡΠΎΡΠΎ, ΠΌΡ Π΄ΠΎΠ»ΠΆΠ½Ρ ΡΠ²ΠΈΠ΄Π΅ΡΡ ΡΡΠΎ.
ΠΠΎΠ΄Π΅Π»ΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ Π²ΠΎΠ»Π½Ρ Ocean
ΠΠΎΠ·Π²ΠΎΠ»ΡΡΠ΅ ΠΌΠ½Π΅ ΡΠ»ΠΎΠΌΠ°ΡΡ ΡΠΎ, ΡΡΠΎ Π·Π΄Π΅ΡΡ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ.
- 1οΈβ£. ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ React Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅Ρ ΡΠ»Π΅ΠΌΠ΅Π½Ρ Ρ ΠΎΠ»ΡΡΠ° Π΄Π»Ρ ΡΡΠ΅Π½Ρ 3D WebGL
- 2οΈβ£. ΠΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ React’s
Π£Π‘ΠΠ ΠΠ€ΠΠ»Ρ Π΄ΠΎΡΡΡΠΏΠ° ΠΊ HTML-ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ Canvas - 3οΈβ£. ΠΡ ΡΠΎΠ·Π΄Π°Π»ΠΈ Π½ΠΎΠ²ΡΡ
Π‘ΠΈΠΌΡΠ»ΡΡΠΎΡΠΊΠΎΠ³Π΄Π° ΡΡΡΠ»ΠΎΡΠ½ΡΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΡΡΡ.Π‘ΠΈΠΌΡΠ»ΡΡΠΎΡΠΠ»Π°ΡΡ Π½Π΅ΡΠ΅Ρ ΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎΡΡΡ Π·Π° ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³ΠΎΠΌ, Π° ΡΠ°ΠΊΠΆΠ΅ Π½Π΅Π΄Π²ΠΈΠΆΠΈΠΌΠΎΡΡΡ ΠΏΠΎΠ³ΠΎΠ΄Π½ΠΎΡΡΠΈ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ Π²Π΅ΡΠ΅Ρ , Choppiness ΠΈ Π Π°Π·ΠΌΠ΅Ρ ΠΡΠΊΠ°Π· - 4οΈβ£ ΠΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ
UserAf(ProcessanimationFrame) ΠΊΡΡΡΠΎΠΊ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠΈΠΊΠ»Π°, Π³Π΄Π΅ Π²ΡΠΏΠΎΠ»Π½ΡΠ΅ΡΡΡ ΠΎΠ±ΡΠ°ΡΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠ°Π΄ΡΠ΅ Π°Π½ΠΈΠΌΠ°ΡΠΈΠΈ.
ΠΠ° Π΄Π°Π½Π½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ Π½Π°ΡΠΈ ΠΎΠΊΠ΅Π°Π½ΡΠΊΠΈΠ΅ Π²ΠΎΠ»Π½Ρ Π΄Π²ΠΈΠΆΡΡΡΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈΡ
Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΠΏΠΎΠ³ΠΎΠ΄Ρ: ΠΌΠ΅Π»ΠΊΠΎΠ²ΠΎΠ΄ΡΠ΅ , Π²Π΅ΡΠ΅Ρ ΠΈ Π Π°Π·ΠΌΠ΅Ρ ΠΡΠΊΠ°Π· ΠΡΠ°ΠΊ, ΠΊΠ°ΠΊ ΠΌΡ ΡΠ°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΡΠΈ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΏΠΎΠ³ΠΎΠ΄Ρ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΡΠΏΠΎΠΊΠΎΠΉΡΡΠ²ΠΈΠ΅ ΡΡΠ΅Ρ?
ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Ρ ΡΠΎΠ·Π΄Π°Π» ΡΡΠ½ΠΊΡΠΈΡ ΡΡΠΈΠ»ΠΈΡΡ Π² Weather.js ΠΠ»Ρ ΡΠΎΠΏΠΎΡΡΠ°Π²Π»Π΅Π½ΠΈΡ ΡΠΏΠΎΠΊΠΎΠΉΡΡΠ²ΠΈΠ΅ ΠΡΠ΅Π½ΠΊΠ° ΠΊ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΠΌ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°ΠΌ ΠΏΠΎΠ³ΠΎΠ΄Ρ: ΠΌΠ΅Π»ΠΊΠΎΠ²ΠΎΠ΄ΡΠ΅ , Π²Π΅ΡΠ΅Ρ ΠΈ Π Π°Π·ΠΌΠ΅Ρ ΠΡΠΊΠ°Π· Π Π·Π°ΡΠ΅ΠΌ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ·Π΄Π°ΡΡ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅Ρ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Π· Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ ΠΡΠ΅Π½ΠΊΠ° ΠΌΠ΅Π½ΡΠ΅ΡΡΡ.
useEffect(() => {
if (simulator) {
setWeatherBasedOnCalm(animatedCalm, 0, 0);
}
function setWeatherBasedOnCalm(calm) {
const { choppiness, wind, size } = mapCalmToWeather(calm);
simulator.setChoppiness(choppiness);
simulator.setWind(wind, wind);
simulator.setSize(size);
}
}, [calm, simulator]);
ΠΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅
ΠΡΠΎ Π²Π΅ΡΠ΅Π»Π°Ρ ΡΠ°ΡΡΡ. ΠΠΌΠ΅Π½Π½ΠΎ Π·Π΄Π΅ΡΡ ΠΌΡ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄ΠΎΡΡΡΠΏ ΠΊ Π΄Π°Π½Π½ΡΠΌ ΠΌΠΎΠ·Π³Π° ΠΈ ΡΠΎΠΏΠΎΡΡΠ°Π²ΡΡΠ΅ Π΅Π³ΠΎ Π² ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ.
ΠΠΎΠ΄ΠΏΠΈΡΠ°Π²ΡΠΈΡΡ Π½Π° Notion.Calm () ΠΌΡ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ Π½ΠΎΠ²ΡΠΉ Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ ΠΡΠ΅Π½ΠΊΠ° ΠΏΡΠΈΠΌΠ΅ΡΠ½ΠΎ ΠΊΠ°ΠΆΠ΄ΡΡ ΡΠ΅ΠΊΡΠ½Π΄Ρ. ΠΡΠ°ΠΊ, Π΄Π°Π²Π°ΠΉΡΠ΅ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ, Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΎΠΏΠΎΡΡ ΠΈ ΡΠΎΠ·Π΄Π°ΡΡ ΠΏΠΎΠ±ΠΎΡΠ½ΡΠΉ ΡΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΡΠ΅ΡΡΡ Ρ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡΠΎΠΌ ΠΠΎΠ½ΡΡΠΈΠ΅ ΠΈ Ρ ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΡΠΊΠ°Π· ΠΡΠ»ΠΈ ΡΡΠΈ Π΄Π²Π° ΡΠΎΡΡΠΎΡΠ½ΠΈΡ ΠΏΡΠΈΡΡΡΡΡΠ²ΡΡΡ, ΡΠΎ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎ ΠΏΠΎΠ΄ΠΏΠΈΡΠ°ΡΡΡΡ Π½Π° Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ ΠΡΠΊΠ°Π·
π§πΏβοΈ. Π‘ΠΏΠΎΠΊΠΎΠΉΠ½Π°Ρ ΠΎΡΠ΅Π½ΠΊΠ° ΠΏΠΎΠ»ΡΡΠ΅Π½Π° ΠΈΠ· Π²Π°ΡΠ΅Π³ΠΎ ΠΏΠ°ΡΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ³Π½ΠΈΡΠΈΠ²Π½ΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ. ΠΡΠ° ΠΌΠ΅ΡΡΠΈΠΊΠ° Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΠ»ΡΡΠ°-ΠΠΎΠ»Π½Π° ΠΡΠΊΠ°Π· Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠ΅ ΠΎΡΠ΅Π½ΠΊΠΈ Π²Π°ΡΡΠΈΡΡΡΡΡΡ ΠΎΡ 0,0 ΠΊ 1.0 . Π§Π΅ΠΌ Π²ΡΡΠ΅ ΠΎΡΠ΅Π½ΠΊΠ°, ΡΠ΅ΠΌ Π²ΡΡΠ΅ Π²Π΅ΡΠΎΡΡΠ½ΠΎΡΡΡ A Π‘ΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ Π§ΡΠ²ΡΡΠ²ΠΎ ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΎ. ΠΠΎΠ»ΡΡΠ΅Π½ΠΈΠ΅ ΡΠΏΠΎΠΊΠΎΠΉΠ½ΠΎΠΉ ΠΎΡΠ΅Π½ΠΊΠΈ Π½Π°Π΄ 0,3 Π·Π½Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎ. ΠΠ΅ΡΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΠΎΠ³ΡΡ ΠΏΠΎΠΌΠΎΡΡ ΡΠ²Π΅Π»ΠΈΡΠΈΡΡ ΡΠΏΠΎΠΊΠΎΠΉΠ½ΡΠΉ Π±Π°Π»Π», Π²ΠΊΠ»ΡΡΠ°ΡΡ Π·Π°ΠΊΡΡΡΠΈΠ΅ Π³Π»Π°Π·, ΡΠΎΡ
ΡΠ°Π½ΡΡ Π²ΡΠ΅ Π΅ΡΠ΅, Π΄ΡΡΠ°ΡΡ Π³Π»ΡΠ±ΠΎΠΊΠΎ ΠΈΠ»ΠΈ ΠΌΠ΅Π΄ΠΈΡΠΈΡΠΎΠ²Π°ΡΡ.
// src/pages/Calm.js
import React, { useState, useEffect } from "react";
import { Ocean } from "../components/Ocean/Ocean";
export function Calm({ user, notion }) {
const [calm, setCalm] = useState(0);
useEffect(() => {
if (!user || !notion) {
return;
}
const subscription = notion.calm().subscribe(calm => {
const calmScore = Number(calm.probability.toFixed(2));
setCalm(calmScore);
});
return () => {
subscription.unsubscribe();
};
}, [user, notion]);
return (
);
}
π‘ ΠΡΠ΅ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎΠ½ΡΡΠΈΡ, Π² ΡΠΎΠΌ ΡΠΈΡΠ»Π΅ Notion.Calm () ΠΠ΅ΡΠ½ΠΈΡΠ΅ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΡ RXJS, ΠΊΠΎΡΠΎΡΡΡ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Π΄Π»Ρ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΠΉ ΠΎΡΠΏΠΈΡΠ°Π½ΠΈΡ, ΠΊΠΎΠ³Π΄Π° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ ΡΠ°Π·ΠΌΠΎΠ½ΡΠΈΡΡΠ΅Ρ.
Π, Π½Π°ΠΊΠΎΠ½Π΅Ρ, ΠΌΡ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅ΠΌ Π½Π°ΡΡ ΡΠΏΠΎΠΊΠΎΠΉΠ½ΡΡ ΡΡΡΠ°Π½ΠΈΡΡ Π² App.js ΠΡΠΊΠ°Π·
// src/App.js
// ...
import { Calm } from "./pages/Calm";
// ...
// If already authenticated, redirect user to the Calm page
useEffect(() => {
if (user) {
navigate("/calm");
}
}, [user]);
return (
{/* ... */}
);
Π Ρ ΡΡΠΈΠΌ Π½Π°ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Neuro React ΡΠ΅ΠΏΠ΅ΡΡ Π·Π°Π²Π΅ΡΡΠ΅Π½ΠΎ.
ΠΠ΅Π²ΡΠ°ΡΡΠΎΠ²ΠΎΡΡΡ/ΠΏΠΎΠ½ΡΡΠΈΠ΅-ΠΎΠΊΠ΅Π°Π½
π ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΠΌΠΎΠ·Π³ΠΎΠ²ΠΎΠΉ ΠΊΠΎΠΌΠΏΡΡΡΠ΅Ρ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ΠΌ WebGL ΠΎΠΊΠ΅Π°Π½Π°
Π― Π²Π·Π²ΠΎΠ»Π½ΠΎΠ²Π°Π½ ΠΎΠΏΡΡΠΎΠΌ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ, Π½Π° ΠΊΠΎΡΠΎΡΡΠ΅ Π²Π»ΠΈΡΡΡ, ΠΊΡΠΎ ΠΌΡ ΡΠ²Π»ΡΠ΅ΠΌΡΡ ΡΠ΅Π»ΠΎΠ²Π΅ΠΊΠΎΠΌ. ΠΠ°ΠΆΠ΄ΡΠΉ ΠΌΠΎΠ·Π³ ΠΎΡΠ»ΠΈΡΠ°Π΅ΡΡΡ, Π½ΠΎ ΠΌΡ ΡΠΎΡ ΡΠ°Π½ΡΠ΅ΠΌ ΡΡΡΠΎΠΈΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»ΡΡΡ ΡΠΎΡ ΠΆΠ΅ ΠΎΠΏΡΡ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ. Π§ΡΠΎ Π΅ΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ Π±ΡΠ»ΠΈ Π°Π΄Π°ΠΏΡΠΈΡΠΎΠ²Π°Π½Ρ ΠΊ ΠΡ ΠΡΠΏΠΎΠΌΠΎΠ³Π°ΡΠ΅Π»ΡΠ½ΡΠΉ
Π§ΡΠΎ Π΅ΡΠ»ΠΈ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ ΠΌΠΎΠ³ΡΡ ΠΏΠΎΠΌΠΎΡΡ Π²Π°ΠΌ ΡΠ°ΡΡΠ»Π°Π±ΠΈΡΡΡΡ, ΠΊΠΎΠ³Π΄Π° Π²Ρ ΠΏΠΎΠ΄ΡΠ΅ΡΠΊΠΈΠ²Π°Π΅ΡΠ΅ΡΡ?
Π§ΡΠΎ, Π΅ΡΠ»ΠΈ Π²Ρ ΠΌΠΎΠΆΠ΅ΡΠ΅ Π°ΡΡΠ΅Π½ΡΠΈΡΠΈΡΠΈΡΠΎΠ²Π°ΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Ρ Π²Π°ΡΠΈΠΌΠΈ ΠΌΠΎΠ·Π³ΠΎΠ²ΡΠΌΠΈ Π²ΠΎΠ»Π½Π°ΠΌΠΈ?
Π§ΡΠΎ Π΅ΡΠ»ΠΈ Π²ΠΈΠ΄Π΅ΠΎΠΈΠ³ΡΡ ΠΌΠΎΠ³ΡΡ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ ΡΠ²ΠΎΠΉ ΡΠ°ΡΡΠΊΠ°Π· Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ Π²Π°ΡΠΈΡ ΡΡΠ²ΡΡΠ²?
Π§ΡΠΎ, Π΅ΡΠ»ΠΈ…
ΠΡΠΈΠ³ΠΈΠ½Π°Π»: “https://dev.to/neurosity/building-your-first-neuro-app-with-react-49pj”