react使用hooks案例
 2020/7/21  react
# asyncDelay
// 测试延迟
const asyncDelay = ms => new Promise(r => setTimeout(r, ms));
1
2
2
# sleep
function sleep(sleepTime){
    var start=new Date().getTime();
    while(true){
        if(new Date().getTime()-start>sleepTime){
            break;
        }
    }
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Portal
import React from "react";
import { createPortal } from "react-dom";
class Dialog extends React.Component {
  constructor() {
    super(...arguments);
    const doc = window.document;
    this.node = doc.createElement("div");
    doc.body.appendChild(this.node);
  }
  render() {
    return createPortal(
      <div class="dialog">{this.props.children}</div>, //塞进传送门的JSX
      this.node //传送门的另一端DOM node
    );
  }
  componentWillUnmount() {
    window.document.body.removeChild(this.node);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# useModal
- create Modal.js
// Modal.js
import React from 'react'
import ReactDOM from 'react-dom'
const Modal = React.memo(({ children, closeModal }) => {
  const domEl = document.getElementById('modal-root')
  if (!domEl) return null
  return ReactDOM.createPortal(
    <div>
      <button onClick={closeModal}>Close</button>
      {children}
    </div>,
    domEl
  )
})
export default Modal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- create useModal.js
// useModal.js
import React, { useState } from "react";
import Modal from "./Modal";
// Modal组件最基础的两个事件,show/hide
export const useModal = () => {
  const [isVisible, setIsVisible] = useState(false);
  const show = () => setIsVisible(true);
  const hide = () => setIsVisible(false);
  const RenderModal = ({ children }: { children: React.ReactChild }) => (
    <React.Fragment>
      {isVisible && <Modal closeModal={hide}>{children}</Modal>}
    </React.Fragment>
  );
  return {
    show,
    hide,
    RenderModal
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- use modal
import React from "react";
import { useModal } from "./useModal";
const App = React.memo(() => {
  const { show, hide, RenderModal } = useModal();
  return (
    <div>
      <div>
        <p>some content...</p>
        <button onClick={show}>打开</button>
        <button onClick={hide}>关闭</button>
        <RenderModal>
          <p>这里面的内容将会被渲染到'modal-root'容器里.</p>
        </RenderModal>
      </div>
      <div id="modal-root" />
    </div>
  );
});
export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# hooks-reducer
- create store/context.js
import { createContext } from "react";
const Context = createContext();
export default Context;
1
2
3
4
5
2
3
4
5
- create store/redux.js
import React, { useReducer } from "react";
import ContextContainer from "./context";
const defaultState = {
  count: 0
};
function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { ...state, count: state.count + 1 };
    case "decrement":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}
const ContextProvider = props => {
  const [state, dispatch] = useReducer(reducer, defaultState);
  return (
    <ContextContainer.Provider value={{ state, dispatch }}>
      {props.children}
    </ContextContainer.Provider>
  );
};
export { reducer, ContextProvider };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- use index.js
import React from "react";
import ReactDOM from "react-dom";
import { ContextProvider } from "./store/redux";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <ContextProvider>
      <App />
    </ContextProvider>
  </React.StrictMode>,
  rootElement
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- use App.js
import React, { useContext } from "react";
import "./styles.css";
import CounterContext from "./store/context";
export default function App() {
  const { state, dispatch } = useContext(CounterContext);
  return (
    <div className="App">
      <h1>React Hooks</h1>
      <h2>useContext and useReducer</h2>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+1</button>
      <span> - </span>
      <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# hooks-redux
- create store/index.js
import { combineReducer } from "./redux";
import User from "./userModule";
import Counter from "./counterModule";
const state = {
  user: User.state,
  counter: Counter.state
};
const reducers = combineReducer({
  user: User.reducer,
  counter: Counter.reducer
});
export default {
  state,
  reducers
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- create store/redux.js
import React, { useContext, useReducer } from "react";
const EMPTY = Symbol("store context");
const StoreContext = React.createContext(EMPTY);
export const Provider = ({ children, store }) => {
  // console.log(store)
  const [state, dispatch] = useReducer(store.reducers, store.state);
  return (
    <StoreContext.Provider value={{ state, dispatch }}>
      {children}
    </StoreContext.Provider>
  );
};
export const useRedux = () => {
  const store = useContext(StoreContext);
  if (store === EMPTY) {
    throw new Error("App Component must be wrapped with <Provider>");
  } else {
    const { state, dispatch } = store;
    return { state, dispatch };
  }
};
export const combineReducer = reducers => {
  return (state = {}, action) => {
    let result = Object.keys(reducers).reduce((newState, key) => {
      newState[key] = reducers[key](state[key], action);
      return newState;
    }, {});
    return result;
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- create store/counterModule.js
const CounterModule = {
  state: {
    count: 0
  },
  reducer: (state, action) => {
    switch (action.type) {
      case "increment":
        return { ...state, count: state.count + 1 };
      case "decrement":
        return { ...state, count: state.count - 1 };
      default:
        return state;
    }
  }
};
export default CounterModule;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- create store/UserModule.js
const UserModule = {
  state: {
    login: "未登录"
  },
  reducer: (state, action) => {
    switch (action.type) {
      case "SET_TOKEN":
        return { ...state, login: "已登录" };
      case "RESET_TOKEN":
        return { ...state, login: "未登录" };
      default:
        return state;
    }
  }
};
export default UserModule;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- use index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "./store/redux";
import store from "./store/index";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  rootElement
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- use App.js
import React from "react";
import "./styles.css";
import { useRedux } from "./store/redux";
export default function App() {
  const { state, dispatch } = useRedux();
  return (
    <div className="App">
      <h1>React Hooks</h1>
      <h2>useContext and useReducer</h2>
      <p>Count:{state.counter.count}</p>
      <button onClick={() => dispatch({ type: "increment" })}>+1</button>
      <span> - </span>
      <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
      <hr />
      <p>login:{state.user.login}</p>
      <button onClick={() => dispatch({ type: "SET_TOKEN" })}>登录</button>
      <span> - </span>
      <button onClick={() => dispatch({ type: "RESET_TOKEN" })}>退出</button>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# hooks-unstated-next
- create store/index.js
import { useState } from "react";
import { createContainer } from "./redux";
const defaultState = {
  count: 0
};
function Store(initialState = defaultState) {
  let [state, setState] = useState(initialState);
  let increment = () => setState(pre => ({ count: pre.count + 1 }));
  let decrement = () => setState(pre => ({ count: pre.count - 1 }));
  return {
    state,
    increment,
    decrement
  };
}
export default createContainer(Store);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- create store/redux.js
// https://github.com/jamiebuilds/unstated-next
import React from "react";
const EMPTY = Symbol("unstated-next");
export function createContainer(useHook) {
  let Context = React.createContext(EMPTY);
  function Provider(props) {
    let value = useHook(props.initialState);
    return <Context.Provider value={value}>{props.children}</Context.Provider>;
  }
  // eslint-disable-next-line no-shadow
  function useContainer() {
    let value = React.useContext(Context);
    if (value === EMPTY) {
      throw new Error("Component must be wrapped with <Container.Provider>");
    }
    return value;
  }
  return { Provider, useContainer };
}
export function useContainer(container) {
  return container.useContainer();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- use index.js
import React from "react";
import ReactDOM from "react-dom";
import Counter from "./store/index";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <Counter.Provider>
      <App />
    </Counter.Provider>
  </React.StrictMode>,
  rootElement
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
- use App.js
import React from "react";
import "./styles.css";
import Counter from "./store/index";
export default function App() {
  const store = Counter.useContainer();
  return (
    <div className="App">
      <h1>React Hooks</h1>
      <h2>use unstated-next</h2>
      <p>Count: {store.state.count}</p>
      <div>
        <button onClick={store.increment}>+1</button> ~{" "}
        <button onClick={store.decrement}>-1</button>
      </div>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
