01. Intro to React Hooks
02. Create your first React app
npx create-react-app my-app
03. React Component
import React from "react";
import "./myButton.css";
const MyButtonComponent = () => {
return (
<button
className="button"
style={{
borderRadius: "30px",
}}
>
Click Me
</button>
);
};
export default MyButtonComponent;
04. Styling in React
npm install styled-components
05. Styles and Props
import React from "react";
import styled from "styled-components";
const MyButtonComponent = () => {
return (
<div className="">
<Button>Click Me</Button>
<Button disabled>Click Me</Button>
</div>
);
};
const Button = styled.button`
background: ${(props) =>
props.disabled
? "gray"
: "linear-gradient(91.4deg, #2fb8ff 0%,#9eecd9 100%)"};
padding: 12px 0;
width: 200px;
border: none;
border-radius: 30px;
color: white;
font-weight: bold;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
`;
export default MyButtonComponent;
06. Understanding Hooks
07. useState Hook
2017 年推出 ussState
import React, { useState } from "react";
import styled from "styled-components";
const MyButtonComponent = () => {
const [count, setCount] = useState(0);
return (
<div className="">
<Button onClick={() => setCount(count + 1)}>Click Me</Button>
<Button disabled>{count}</Button>
</div>
);
};
// ...
08. useEffect Hook
2018 年推出
import React, { useEffect, useState } from "react";
import styled from "styled-components";
const MyButtonComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
// ...
);
};
// ...
09. useRef Hook
import React, { useEffect, useState } from "react";
import styled from "styled-components";
const MyButtonComponent = () => {
useEffect(() => {
const btn = document.getElementById("btn");
if (btn) {
btn.click();
}
});
return (
<div className="">
<Button
id="btn"
onClick={() => {
alert("You clicked!");
}}
>
Click Me
</Button>
</div>
);
};
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
const MyButtonComponent = () => {
const ref = useRef(null);
useEffect(() => {
if (ref) {
ref.current.click();
}
});
return (
<div className="">
<Button
ref={ref}
onClick={() => {
alert("You clicked!");
}}
>
Click Me
</Button>
</div>
);
};
// ...
10. Props
import styled from "styled-components";
const MyButton = (props) => {
return (
<div className="">
<Button isActive={props.isActive}>Click Me</Button>
</div>
);
};
// ...
import React from "react";
import styled from "styled-components";
import MyButton from "./MyButton";
const MyCard = () => {
return (
<Wrapper>
This is my card
<MyButton isActive={false} />
</Wrapper>
);
};
const Wrapper = styled.div`
background: rgba(255, 255, 255, 0.6);
box-shadow: inset 0 0 0 0.5px rgba(255, 255, 255, 0.6);
border-radius: 30px;
padding: 20px;
width: 300px;
height: 150px;
display: grid;
justify-items: center;
align-items: start;
font-family: Segoe UI, sans-serif;
font-weight: bold;
`;
export default MyCard;
11. Conditional Rendering
条件渲染
12. Load Local Data
import MyButton from "./MyButton";
import { menuData } from "./menuData";
function App() {
return (
<div className="App">
<div className="">
{menuData.map((data, i) => {
return <MyButton key={i} title={data.title} image={data.image} />;
})}
</div>
</div>
);
}
export default App;
export const menuData = [
{
title: "Courses",
image: "",
},
{
title: "Courses",
image: "",
},
{
title: "Courses",
image: "",
},
{
title: "Courses",
image: "",
},
];
13. Fetch Data from an API
import { useEffect, useState } from "react";
function App() {
const [bname, setBname] = useState("");
useEffect(() => {
const url = "http://localhost:4000/books";
const fetchData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
setBname(json[0].name);
} catch (e) {
console.log(e);
}
};
fetchData();
});
return <div className="App">{bname}</div>;
}
export default App;
14. Toggle a State
import React, { useState } from "react";
import MyButton from "./MyButton";
import { menuData } from "./menuData";
const Menu = () => {
const [open, setOpen] = useState(false);
return (
<div>
<div className="">
<MyButton
onClick={() => setOpen(!open)}
title="menu"
img="http://localhost:4000/xxx.png"
/>
{open && (
<div>
{menuData.map((m, i) => {
return <MyButton title={m.title} img={m.image} key={i} />;
})}
</div>
)}
</div>
</div>
);
};
export default Menu;
15. useInput Hook
import React from "react";
import useInput from "./useInput";
const SignForm = () => {
const email = useInput("");
const pass = useInput("");
return (
<form
onSubmit={(e) => {
e.preventDefault();
console.log(email.value, pass.value);
}}
>
<h2>Sign In</h2>
<input placeholder="email" {...email} />
<input
placeholder="password"
type="password"
value={pass.value}
onChange={pass.onChange}
/>
<button type="submit">sign in</button>
</form>
);
};
export default SignForm;
import { useState } from "react";
const useInput = (initVal) => {
const [value, setVal] = useState(initVal);
const handleChange = (e) => {
setVal(e.target.value);
};
return {
value,
onChange: handleChange,
};
};
export default useInput;
16. Gatsby and React
npm i -g gatsby-cli
gatsby new my-default-starter https://github.com/gatsbyjs/gatsby-starter-default
gatsby new mysite https://github.com/antvis/gatsby-starter-theme-antv
gatsby build && gatsby serve
17. NextJS and React
npx create-react-app my-nextjs-app
cd my-nextjs-app
npm i next react react-dom
18. React TypeScript Part 1
npx create-react-app my-app --template typescript
跳过
20. useScrollPosition hook
import useScrollPosition from "./useScrollPosition";
function App() {
const position = useScrollPosition();
console.log(position);
// console.log(window.pageYOffset);
// console.log(window.pageYOffset == position);
return (
<div className="App" style={{ margin: 20 }}>
// 使用lorem生成的一堆文本
</div>
);
}
export default App;
import { useEffect, useState } from "react";
const useScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
const updatePosition = () => {
// setScrollPosition(window.pageYOffset)
setScrollPosition(window.scrollY);
};
window.addEventListener("scroll", updatePosition);
return () => window.removeEventListener("scroll", updatePosition);
}, []);
return scrollPosition;
};
export default useScrollPosition;
21. useOnScreen hook
import useOnScreen from "./useOnScreen";
import styled from "styled-components";
function App() {
const ref = useRef(null);
const isVisible = useOnScreen(ref);
console.log(isVisible);
return (
<div className="App" ref={ref}>
<Image
isVisible={isVisible}
style={{
marginTop: 100,
width: 200,
height: 100,
}}
src="https://img.zcool.cn/community/019e655d146371a8012051cd3977b1.jpg@2o.jpg"
></Image>
</div>
);
}
const Image = styled.img`
display: ${(props) => (props.isVisible ? "static" : "none")};
animation: ${(props) => props.isVisible && "scale 5s 1"};
@keyframes scale {
0% {
opacity: 0;
}
50% {
opacity: 0.2;
}
100% {
opacity: 1;
}
}
`;
export default App;
import { useEffect, useState } from "react";
const useOnScreen = (ref, rootMargin = "0px") => {
const [isVisible, setVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
setVisible(entry.isIntersecting);
},
{ rootMargin }
);
const currentElement = ref?.current;
if (currentElement) {
observer.observe(currentElement);
}
return () => {
observer.unobserve(currentElement);
};
});
return isVisible;
};
export default useOnScreen;
22. useContext Hook
跨组件共享状态
import React, { createContext, useContext, useState } from "react";
export const themes = {
light: {
foreground: "#000000",
background: "#eeeeee",
},
dark: {
foreground: "#ffffff",
background: "#222222",
},
};
const initialState = {
theme: themes.light,
setTheme: () => {},
};
const ThemeContext = createContext(initialState);
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState(themes.light);
return (
// 将theme状态和改变状态的方法传给子组件children
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
// 子组件使用theme状态
const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};
export default useTheme;
import MyButton from "./MyButton";
import { ThemeProvider } from "./ThemeContext";
function App() {
return (
<ThemeProvider>
<div>
<h1>useContext</h1>
<MyButton />
</div>
</ThemeProvider>
);
}
export default App;
import React from "react";
import styled from "styled-components";
import useTheme, { themes } from "./ThemeContext";
const MyButton = () => {
const { theme, setTheme } = useTheme();
return (
<Button
onClick={() => {
setTheme(theme === themes.light ? themes.dark : themes.light);
}}
color={theme.foreground}
bgc={theme.background}
>
a
</Button>
);
};
const Button = styled.button`
background: ${(props) => props.bgc};
color: ${(props) => props.color};
padding: 12px 0;
width: 200px;
border: none;
border-radius: 30px;
font-weight: bold;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
`;
export default MyButton;
23. Fragments
和<></>一样,当返回多个组件时,用 fragments 充当根组件,而不是用一个 div 或其他元素包裹,可以减少 dom 开销
24. Lazy Loading
延迟加载(offset)
import LazyLoad from "react-lazyload";
function App() {
return (
<div>
<LazyLoad height={300} offset={100}>
<img
style={{
margin: "30px",
height: 300,
}}
src="https://s2.ax1x.com/2019/12/26/lk6h6g.jpg"
alt=""
/>
</LazyLoad>
<LazyLoad height={300} offset={100}>
<img
style={{
margin: "30px",
height: 300,
}}
src="https://s2.ax1x.com/2019/12/26/lk6h6g.jpg"
alt=""
/>
</LazyLoad>
<LazyLoad height={300} offset={100}>
<img
style={{
margin: "30px",
height: 300,
}}
src="https://s2.ax1x.com/2019/12/26/lk6h6g.jpg"
alt=""
/>
</LazyLoad>
</div>
);
}
export default App;
25. React Suspense
import React, { useEffect, useRef, useState } from "react";
const ActiveCard = () => {
const [active, setActive] = useState(useRef([]).current);
useEffect(() => {
const url = "http://localhost:4000/books";
const fetchData = async () => {
try {
const response = await fetch(url);
const json = await response.json();
console.log(json);
setActive([...active, ...json]);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
return (
<div>
<ul>
{active.map((a) => (
<li key={a.id}>{a.name}</li>
))}
</ul>
</div>
);
};
export default ActiveCard;
import React, { Suspense } from "react";
const ActiveCard = React.lazy(() => import("./ActiveCard"));
function App() {
return (
<div>
<Suspense fallback={<p>Loading ...</p>}>
<ActiveCard />
</Suspense>
</div>
);
}
export default App;
26. Environment Variables
27. Reach Router
课程介绍的已经过时,这个是”react-router-dom”: “^6.14.2”,
import React, { Suspense } from "react";
import { Route, BrowserRouter as Router, Routes } from "react-router-dom";
import Home from "./Home";
import Course from "./Course";
function App() {
return (
<Router>
<Routes>
<Route path="/" exact element={<Home />} />
<Route path="/course" element={<Course />} />
</Routes>
</Router>
);
}
export default App;