reducers
This commit is contained in:
parent
7d6072383d
commit
9149538440
1
usereducer-starting-project/.eslintcache
Normal file
1
usereducer-starting-project/.eslintcache
Normal file
@ -0,0 +1 @@
|
||||
[{"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/index.js":"1","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/App.js":"2","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Home/Home.js":"3","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Login/Login.js":"4","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/MainHeader.js":"5","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/Navigation.js":"6","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Card/Card.js":"7","/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Button/Button.js":"8"},{"size":206,"mtime":1648532752000,"results":"9","hashOfConfig":"10"},{"size":1093,"mtime":1616593928000,"results":"11","hashOfConfig":"10"},{"size":250,"mtime":1616593928000,"results":"12","hashOfConfig":"10"},{"size":3876,"mtime":1663871762917,"results":"13","hashOfConfig":"10"},{"size":368,"mtime":1616593928000,"results":"14","hashOfConfig":"10"},{"size":571,"mtime":1616593928000,"results":"15","hashOfConfig":"10"},{"size":218,"mtime":1616593928000,"results":"16","hashOfConfig":"10"},{"size":353,"mtime":1616593928000,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"4a8obe",{"filePath":"20","messages":"21","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"22","messages":"23","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"24","messages":"25","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"26","messages":"27","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"30","messages":"31","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"34"},"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/index.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/App.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Home/Home.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/Login/Login.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/MainHeader.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/MainHeader/Navigation.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Card/Card.js",[],"/Users/tyrel.souza/code/udemy/react-course/usereducer-starting-project/src/components/UI/Button/Button.js",[],["35","36"],{"ruleId":"37","replacedBy":"38"},{"ruleId":"39","replacedBy":"40"},"no-native-reassign",["41"],"no-negated-in-lhs",["42"],"no-global-assign","no-unsafe-negation"]
|
6
usereducer-starting-project/.prettierrc.json
Normal file
6
usereducer-starting-project/.prettierrc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": false
|
||||
}
|
38145
usereducer-starting-project/package-lock.json
generated
Normal file
38145
usereducer-starting-project/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
usereducer-starting-project/package.json
Normal file
38
usereducer-starting-project/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "react-complete-guide",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
"@testing-library/user-event": "^12.5.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-scripts": "4.0.1",
|
||||
"web-vitals": "^0.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
BIN
usereducer-starting-project/public/favicon.ico
Normal file
BIN
usereducer-starting-project/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
43
usereducer-starting-project/public/index.html
Normal file
43
usereducer-starting-project/public/index.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
BIN
usereducer-starting-project/public/logo192.png
Normal file
BIN
usereducer-starting-project/public/logo192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
usereducer-starting-project/public/logo512.png
Normal file
BIN
usereducer-starting-project/public/logo512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
25
usereducer-starting-project/public/manifest.json
Normal file
25
usereducer-starting-project/public/manifest.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
3
usereducer-starting-project/public/robots.txt
Normal file
3
usereducer-starting-project/public/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
41
usereducer-starting-project/src/App.js
Normal file
41
usereducer-starting-project/src/App.js
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import Login from './components/Login/Login';
|
||||
import Home from './components/Home/Home';
|
||||
import MainHeader from './components/MainHeader/MainHeader';
|
||||
|
||||
function App() {
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const storedUserLoggedInInformation = localStorage.getItem('isLoggedIn');
|
||||
|
||||
if (storedUserLoggedInInformation === '1') {
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loginHandler = (email, password) => {
|
||||
// We should of course check email and password
|
||||
// But it's just a dummy/ demo anyways
|
||||
localStorage.setItem('isLoggedIn', '1');
|
||||
setIsLoggedIn(true);
|
||||
};
|
||||
|
||||
const logoutHandler = () => {
|
||||
localStorage.removeItem('isLoggedIn');
|
||||
setIsLoggedIn(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<MainHeader isAuthenticated={isLoggedIn} onLogout={logoutHandler} />
|
||||
<main>
|
||||
{!isLoggedIn && <Login onLogin={loginHandler} />}
|
||||
{isLoggedIn && <Home onLogout={logoutHandler} />}
|
||||
</main>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
14
usereducer-starting-project/src/components/Home/Home.js
Normal file
14
usereducer-starting-project/src/components/Home/Home.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import Card from '../UI/Card/Card';
|
||||
import classes from './Home.module.css';
|
||||
|
||||
const Home = (props) => {
|
||||
return (
|
||||
<Card className={classes.home}>
|
||||
<h1>Welcome back!</h1>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
@ -0,0 +1,7 @@
|
||||
.home {
|
||||
width: 90%;
|
||||
max-width: 40rem;
|
||||
padding: 3rem;
|
||||
margin: 2rem auto;
|
||||
text-align: center;
|
||||
}
|
124
usereducer-starting-project/src/components/Login/Login.js
Normal file
124
usereducer-starting-project/src/components/Login/Login.js
Normal file
@ -0,0 +1,124 @@
|
||||
import React, { useState, useEffect, useReducer } from "react"
|
||||
|
||||
import Card from "../UI/Card/Card"
|
||||
import classes from "./Login.module.css"
|
||||
import Button from "../UI/Button/Button"
|
||||
|
||||
const emailReducer = (state, action) => {
|
||||
if (action.type === "USER_INPUT") {
|
||||
return { value: action.val, isValid: action.val.includes("@") }
|
||||
}
|
||||
if (action.type === "INPUT_BLUR") {
|
||||
return { value: state.value, isValid: state.value.includes("@") }
|
||||
}
|
||||
return { value: "", isValid: false }
|
||||
}
|
||||
|
||||
const passwordReducer = (state, action) => {
|
||||
if (action.type === "USER_INPUT") {
|
||||
return { value: action.val, isValid: action.val.trim().length > 6 }
|
||||
}
|
||||
if (action.type === "INPUT_BLUR") {
|
||||
return { value: state.value, isValid: state.value.trim().length > 6 }
|
||||
}
|
||||
return { value: "", isValid: false }
|
||||
}
|
||||
|
||||
const Login = (props) => {
|
||||
const [formIsValid, setFormIsValid] = useState(false)
|
||||
|
||||
const [emailState, dispatchEmail] = useReducer(emailReducer, {
|
||||
value: "",
|
||||
isValid: false,
|
||||
})
|
||||
|
||||
const [passwordState, dispatchPassword] = useReducer(passwordReducer, {
|
||||
value: "",
|
||||
isValid: false,
|
||||
})
|
||||
|
||||
const {isValid: emailIsValid} = emailState;
|
||||
const {isValid: passwordIsValid} = emailState;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const identifier = setTimeout(() => {
|
||||
setFormIsValid(emailIsValid && passwordIsValid)
|
||||
}, 500)
|
||||
|
||||
return () => {
|
||||
clearTimeout(identifier)
|
||||
}
|
||||
}, [emailIsValid, passwordIsValid])
|
||||
|
||||
const emailChangeHandler = (event) => {
|
||||
dispatchEmail({ type: "USER_INPUT", val: event.target.value })
|
||||
|
||||
// setFormIsValid(event.target.value.includes("@") && passwordState.isValid)
|
||||
}
|
||||
|
||||
const passwordChangeHandler = (event) => {
|
||||
dispatchPassword({ type: "USER_INPUT", val: event.target.value })
|
||||
|
||||
// setFormIsValid(emailState.isValid && event.target.value.trim().length > 6)
|
||||
}
|
||||
|
||||
const validateEmailHandler = () => {
|
||||
dispatchEmail({ type: "INPUT_BLUR" })
|
||||
}
|
||||
|
||||
const validatePasswordHandler = () => {
|
||||
dispatchPassword({ type: "INPUT_BLUR" })
|
||||
}
|
||||
|
||||
const submitHandler = (event) => {
|
||||
event.preventDefault()
|
||||
props.onLogin(emailState.value, passwordState.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className={classes.login}>
|
||||
<form onSubmit={submitHandler}>
|
||||
<div
|
||||
className={`${classes.control} ${
|
||||
!emailState.isValid ? classes.invalid : ""
|
||||
}`}
|
||||
>
|
||||
<label htmlFor="email">E-Mail</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
value={emailState.value}
|
||||
onChange={emailChangeHandler}
|
||||
onBlur={validateEmailHandler}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`${classes.control} ${
|
||||
!passwordState.isValid ? classes.invalid : ""
|
||||
}`}
|
||||
>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
value={passwordState.value}
|
||||
onChange={passwordChangeHandler}
|
||||
onBlur={validatePasswordHandler}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.actions}>
|
||||
<Button
|
||||
type="submit"
|
||||
className={classes.btn}
|
||||
disabled={!formIsValid}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login
|
@ -0,0 +1,56 @@
|
||||
.login {
|
||||
width: 90%;
|
||||
max-width: 40rem;
|
||||
margin: 2rem auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.control {
|
||||
margin: 1rem 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.control label,
|
||||
.control input {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.control label {
|
||||
font-weight: bold;
|
||||
flex: 1;
|
||||
color: #464646;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.control input {
|
||||
flex: 3;
|
||||
font: inherit;
|
||||
padding: 0.35rem 0.35rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.control input:focus {
|
||||
outline: none;
|
||||
border-color: #4f005f;
|
||||
background: #f6dbfc;
|
||||
}
|
||||
|
||||
.control.invalid input {
|
||||
border-color: red;
|
||||
background: #fbdada;
|
||||
}
|
||||
|
||||
.actions {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.control {
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Navigation from './Navigation';
|
||||
import classes from './MainHeader.module.css';
|
||||
|
||||
const MainHeader = (props) => {
|
||||
return (
|
||||
<header className={classes['main-header']}>
|
||||
<h1>A Typical Page</h1>
|
||||
<Navigation isLoggedIn={props.isAuthenticated} onLogout={props.onLogout} />
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainHeader;
|
@ -0,0 +1,16 @@
|
||||
.main-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #741188;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.main-header h1 {
|
||||
color: white;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import classes from './Navigation.module.css';
|
||||
|
||||
const Navigation = (props) => {
|
||||
return (
|
||||
<nav className={classes.nav}>
|
||||
<ul>
|
||||
{props.isLoggedIn && (
|
||||
<li>
|
||||
<a href="/">Users</a>
|
||||
</li>
|
||||
)}
|
||||
{props.isLoggedIn && (
|
||||
<li>
|
||||
<a href="/">Admin</a>
|
||||
</li>
|
||||
)}
|
||||
{props.isLoggedIn && (
|
||||
<li>
|
||||
<button onClick={props.onLogout}>Logout</button>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navigation;
|
@ -0,0 +1,43 @@
|
||||
.nav ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav li {
|
||||
margin: 0;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
.nav a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav a:hover,
|
||||
.nav a:active {
|
||||
color: #f3cafb;
|
||||
}
|
||||
|
||||
.nav button {
|
||||
font: inherit;
|
||||
background: #dd0db0;
|
||||
border: 1px solid #dd0db0;
|
||||
padding: 0.5rem 1.5rem;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.26);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.nav button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.nav button:hover,
|
||||
.nav button:active {
|
||||
color: #f3cafb;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.26);
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
|
||||
import classes from './Button.module.css';
|
||||
|
||||
const Button = (props) => {
|
||||
return (
|
||||
<button
|
||||
type={props.type || 'button'}
|
||||
className={`${classes.button} ${props.className}`}
|
||||
onClick={props.onClick}
|
||||
disabled={props.disabled}
|
||||
>
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
@ -0,0 +1,30 @@
|
||||
.button {
|
||||
font: inherit;
|
||||
border: 1px solid #4f005f;
|
||||
background: #4f005f;
|
||||
color: white;
|
||||
padding: 0.75rem 3.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 1.15rem;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:active {
|
||||
background: #741188;
|
||||
border-color: #741188;
|
||||
}
|
||||
|
||||
.button:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
button:focus:disabled,
|
||||
button:hover:disabled,
|
||||
button:active:disabled {
|
||||
background: #ccc;
|
||||
border-color: #ccc;
|
||||
color: #666666;
|
||||
cursor: not-allowed;
|
||||
}
|
11
usereducer-starting-project/src/components/UI/Card/Card.js
Normal file
11
usereducer-starting-project/src/components/UI/Card/Card.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import classes from './Card.module.css';
|
||||
|
||||
const Card = (props) => {
|
||||
return (
|
||||
<div className={`${classes.card} ${props.className}`}>{props.children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Card;
|
@ -0,0 +1,5 @@
|
||||
.card {
|
||||
background: white;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.26);
|
||||
border-radius: 10px;
|
||||
}
|
16
usereducer-starting-project/src/index.css
Normal file
16
usereducer-starting-project/src/index.css
Normal file
@ -0,0 +1,16 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
margin-top: 6rem;
|
||||
}
|
8
usereducer-starting-project/src/index.js
Normal file
8
usereducer-starting-project/src/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(<App />);
|
Loading…
Reference in New Issue
Block a user