React Hooks Handbook

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

styled

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

gatsby插件

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;

28. URL Params


  转载请注明: malred-blog React Hooks Handbook

  目录