├── public
├── favicon.ico
├── manifest.json
└── index.html
├── src
├── App.test.js
├── index.css
├── index.js
├── page
│ ├── GlobalContext
│ │ ├── index.js
│ │ ├── OtherPerson.js
│ │ ├── All.js
│ │ ├── Person.js
│ │ └── utils.js
│ ├── index.js
│ ├── Home
│ │ ├── App.css
│ │ ├── logo.svg
│ │ └── index.js
│ └── Redux
│ │ ├── App.css
│ │ ├── logo.svg
│ │ └── index.js
├── utils.js
└── serviceWorker.js
├── .gitignore
├── package.json
└── README.md
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/asyalas/react-hook-demo/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div');
7 | ReactDOM.render(, div);
8 | ReactDOM.unmountComponentAtNode(div);
9 | });
10 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
6 | sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './page';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: http://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/src/page/GlobalContext/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import All from "./All";
3 | import OtherPerson from "./OtherPerson";
4 | import Person from "./Person";
5 | const Line = () => (
6 |
13 | );
14 | const Home = () => {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default Home;
28 |
--------------------------------------------------------------------------------
/src/page/GlobalContext/OtherPerson.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createGlobalContext, useContext } from "./utils";
3 | const OtherPerson = () => {
4 | const OtherPersonStore = createGlobalContext("otherPerson", {
5 | age: 19,
6 | name: "nick"
7 | });
8 | return (
9 |
10 |
11 |
12 | );
13 | };
14 | const OtherPersonPage = () => {
15 | const { age, name } = useContext("otherPerson");
16 | return (
17 |
18 |
姓名:{name}
19 |
年龄:{age}
20 |
21 | );
22 | };
23 |
24 | export default OtherPerson;
25 |
--------------------------------------------------------------------------------
/src/page/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Router, Switch, Route } from "react-router-dom";
3 | import Home from "./Home";
4 | import Redux from './Redux'
5 | import GlobalContext from './GlobalContext'
6 | import "antd/dist/antd.min.css";
7 |
8 | import * as history from "history";
9 | export default () => {
10 | return (
11 |
12 |
13 | ;
14 | ;
15 | ;
16 |
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-hook-demo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "antd": "^3.15.1",
7 | "react": "^16.8.5",
8 | "react-dom": "^16.8.5",
9 | "react-redux": "^6.0.1",
10 | "react-router-dom": "^5.0.0",
11 | "react-scripts": "2.1.1",
12 | "redux": "^4.0.1"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "eslintConfig": {
21 | "extends": "react-app"
22 | },
23 | "browserslist": [
24 | ">0.2%",
25 | "not dead",
26 | "not ie <= 11",
27 | "not op_mini all"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/src/page/GlobalContext/All.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useContext } from "./utils";
3 | import { Button } from "antd";
4 | const All = () => {
5 | const globalStates = useContext.getGlobal();
6 | const [state, setState] = React.useState(0);
7 | const addAgeHandle = dispatch => {
8 | dispatch(data => ({
9 | ...data,
10 | age: data.age + 1
11 | }));
12 | setState(state + 1);
13 | };
14 | const dom = Object.entries(globalStates).map(([, v]) => (
15 |
16 | name : {v.name}
17 | age:{v.age}
18 |
19 | value:{JSON.stringify(v.getState())}
20 |
21 |
22 | ));
23 | return dom;
24 | };
25 |
26 | export default All;
27 |
--------------------------------------------------------------------------------
/src/page/GlobalContext/Person.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createGlobalContext, useContext } from "./utils";
3 | import { Button } from "antd";
4 |
5 | const Result = () => {
6 | const { age, name } = useContext("person");
7 | return (
8 |
9 |
姓名:{name}
10 |
年龄:{age}
11 |
12 | );
13 | };
14 |
15 | const AddButton = () => {
16 | const { dispatch } = useContext("person");
17 | const addAgeHandle = () => {
18 | dispatch(data => ({
19 | ...data,
20 | age: data.age + 1
21 | }));
22 | };
23 | return (
24 |
27 | );
28 | };
29 | const Person = () => {
30 | const PersonStore = createGlobalContext("person", {
31 | age: 18,
32 | name: "harry"
33 | });
34 | return (
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default Person;
43 |
--------------------------------------------------------------------------------
/src/page/Home/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | /* background-color: #282c34; */
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-button{
22 | width: 60px;
23 | height: 20px;
24 | background-color: #fff;
25 | /* color: #fff; */
26 | border-radius: 20px;
27 | margin-bottom: 20px;
28 | }
29 | .App-button-group button{
30 | width: 80px;
31 | height: 20px;
32 | background-color: #fff;
33 | /* color: #fff; */
34 | border-radius: 20px;
35 | margin-bottom: 20px;
36 |
37 | }
38 | .App-button-group button+button{
39 | margin-left: 20px
40 | }
41 | .App-link {
42 | /* color: #61dafb; */
43 | }
44 |
45 | @keyframes App-logo-spin {
46 | from {
47 | transform: rotate(0deg);
48 | }
49 | to {
50 | transform: rotate(360deg);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/page/Redux/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | animation: App-logo-spin infinite 20s linear;
7 | height: 40vmin;
8 | }
9 |
10 | .App-header {
11 | /* background-color: #282c34; */
12 | min-height: 100vh;
13 | display: flex;
14 | flex-direction: column;
15 | align-items: center;
16 | justify-content: center;
17 | font-size: calc(10px + 2vmin);
18 | color: white;
19 | }
20 |
21 | .App-button{
22 | width: 60px;
23 | height: 20px;
24 | background-color: #fff;
25 | /* color: #fff; */
26 | border-radius: 20px;
27 | margin-bottom: 20px;
28 | }
29 | .App-button-group button{
30 | width: 80px;
31 | height: 20px;
32 | background-color: #fff;
33 | /* color: #fff; */
34 | border-radius: 20px;
35 | margin-bottom: 20px;
36 |
37 | }
38 | .App-button-group button+button{
39 | margin-left: 20px
40 | }
41 | .App-link {
42 | /* color: #61dafb; */
43 | }
44 |
45 | @keyframes App-logo-spin {
46 | from {
47 | transform: rotate(0deg);
48 | }
49 | to {
50 | transform: rotate(360deg);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/page/GlobalContext/utils.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | let globalContext = {};
3 | export function useContext(namespace) {
4 | const context = globalContext[namespace];
5 | return {
6 | ...React.useContext(context),
7 | dispatch: context.dispatch,
8 | getState: context.getState
9 | };
10 | }
11 | useContext.getGlobal = () =>
12 | Object.keys(globalContext).reduce((p, key) => {
13 | p[key] = useContext(key);
14 | return p;
15 | }, {});
16 | export function createGlobalContext(namespace, initialState) {
17 | const Context = React.createContext(initialState);
18 | if (globalContext.namespace) {
19 | throw new Error("the Context has mounted");
20 | }
21 | globalContext[namespace] = Context;
22 | const NativeProvider = Context.Provider;
23 | Context.Provider = ({ children }) => {
24 | const [state, setState] = React.useState(initialState);
25 |
26 | Context.dispatch = setState;
27 | Context.getState = () => state;
28 | return React.createElement(NativeProvider, { value: state }, children);
29 | };
30 |
31 | Context.dispatch = Context.read = () => {
32 | throw new Error("ContextIO not mount");
33 | };
34 |
35 | return Context;
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef, useState ,useCallback} from "react";
2 |
3 | /**
4 | * 生命周期 componentWillUnmount
5 | * @param fn
6 | */
7 | export const useWillUnmount = fn =>
8 | useEffect(() => {
9 | return () => fn && fn();
10 | }, []);
11 |
12 | /**
13 | * 生命周期 componentDidMount
14 | * @param fn
15 | */
16 | export const useDidMount = fn =>
17 | useEffect(() => {
18 | if (!!fn) {
19 | fn();
20 | }
21 | }, []);
22 |
23 | /**
24 | * 生命周期 componentDidUpdate
25 | * @param fn
26 | */
27 |
28 | export const useDidUpdate = fn => {
29 | const mounting = useRef(true);
30 | useEffect(() => {
31 | if (mounting.current) {
32 | mounting.current = false;
33 | } else {
34 | fn();
35 | }
36 | });
37 | };
38 |
39 | /**
40 | * 强制更新 forceUpdate
41 | */
42 | export const useForceUpdate = () => {
43 | const [, forceUpdate] = useState(0);
44 | return ()=>forceUpdate(Math.random());
45 | };
46 |
47 | /**
48 | * input的onchange事件
49 | * @param {*} initial
50 | */
51 | export const useInputState = (initial) => {
52 | const [value, setValue] = useState(initial);
53 | const onChange =useCallback( (e) => setValue(e.target.value.trim()),[]);
54 | return [value, onChange, setValue];
55 | };
56 |
57 | /**
58 | * 获取更新前的值
59 | * @param {*} value
60 | */
61 | export const usePrevious = value => {
62 | const ref = useRef();
63 | useEffect(() => {
64 | ref.current = value;
65 | });
66 | return ref.current;
67 | };
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/page/Home/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/page/Redux/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 前言
2 |
3 | react作为前端目前很火的框架,拥有者无数的开发者和活跃的社区,但作为开发者在用react开发的时候,你是否遇到过以下的问题:
4 |
5 | - 复用一个有状态的组件太难
6 |
7 | react的核心思想推荐将一个页面拆成一堆独立的,可复用的组件,并且用自上而下的单向数据流的形式将这些组件串联起来。
8 |
9 | 但实践发现很多组件很冗长且带有状态,不好进行拆分,复用困难大
10 |
11 | 于是官方推荐使用HOC
12 |
13 | - 生命周期钩子函数里的逻辑太乱
14 |
15 | 我们希望一个函数只做一件事,但生命周期钩子函数中,其实同时在做多件事情。同时,在某种情景下,我们需要在componentDidMount和componentDidUpdate做同样的事情
16 |
17 | - class
18 |
19 | 我们用class来创建一个组件的时候,最重要的就是给函数绑定this
20 |
21 | ```js
22 | //不管是
23 | this.handleClick = this.handleClick.bind(this)
24 | //还是
25 | const handleClick = ()=>{}
26 | //或者是
27 |