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