react Hooks 在 2018 年年底就已经公布了,正式发布是在 2019 年 5 月,关于它到底能做什么用,并不在本文的探讨范围之内,本文旨在摸索,如何基于 Hooks 以及 Context,实现多组件的状态共享,完成一个精简版的 Redux。
yarn create create-app hooks-context-based-state-management-react-app
cd hooks-context-based-state-management-react-app
yarn start
或者可以直接 clone 本文完成的项目:
git clone https://github.com/pantao/hooks-context-based-state-management-react-app.git
绝大多数情况下,我们其实只需要共享会话状态即可,在本文的示例中,我们也就只共享这个,在 src 目录下,创建一个 store/types.js 文件,它定义我们的 action 类型:
// 设置 session
const SET_SESSION = "SET_TOKEN";
// 销毁会话
const DESTROY_SESSION = "DESTROY_SESSION";
export { SET_SESSION, DESTROY_SESSION };
export default { SET_SESSION, DESTROY_SESSION };
接着定义我们的 src/reducers.js :
import { SET_SESSION, DESTROY_SESSION } from "./types";
const initialState = {
// 会话信息
session: {
// J.W.T Token
token: "",
// 用户信息
user: null,
// 过期时间
expireTime: null
}
};
const reducer = (state = initialState, action) => {
console.log({ oldState: state, ...action });
const { type, payload } = action;
switch (type) {
case SET_SESSION:
return {
...state,
session: {
...state.session,
...payload
}
};
case DESTROY_SESSION:
return {
...state,
session: { ...initialState }
};
default:
throw new Error("Unexpected action");
}
};
export { initialState, reducer };
import { SET_SESSION, DESTROY_SESSION } from "./types";
export const useActions = (state, dispatch) => {
return {
login: async (username, password) => {
console.log(`login with ${username} & ${password}`);
const session = await new Promise(resolve => {
// 模拟接口请求费事 1 秒
setTimeout(
() =>
resolve({
token: "J.W.T",
expireTime: new Date("2030-09-09"),
user: {
username,
password
}
}),
1000
);
});
// dispatch SET_TOKEN
dispatch({
type: SET_SESSION,
payload: session
});
return session;
},
logout: () => {
dispatch({
type: DESTROY_SESSION
});
}
};
};
import React, { createContext, useReducer, useEffect } from "react";
import { reducer, initialState } from "./reducers";
import { useActions } from "./actions";
const StoreContext = createContext(initialState);
function StoreProvider({ children }) {
// 设置 reducer,得到 `dispatch` 方法以及 `state`
const [state, dispatch] = useReducer(reducer, initialState);
// 生成 `actions` 对象
const actions = useActions(state, dispatch);
// 打印出新的 `state`
useEffect(() => {
console.log({ newState: state });
}, [state]);
// 渲染 state, dispatch 以及 actions
return (
<StoreContext.Provider value={{ state, dispatch, actions }}>
{children}
</StoreContext.Provider>
);
}
export { StoreContext, StoreProvider };
打开 src/index.js :
import React from 'react';
import Reactdom from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
做如下修改:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { StoreProvider } from "./context/StoreContext"; // 导入 StoreProvider 组件
ReactDOM.render(
<StoreProvider>
<App />
</StoreProvider>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
内容如下:
import React, { useContext, useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { StoreContext } from "./store/StoreContext";
import { DESTROY_SESSION } from "./store/types";
function App() {
const { state, dispatch, actions } = useContext(StoreContext);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const { user, expireTime } = state.session;
const login = async () => {
if (!username) {
return alert("请输入用户名");
}
if (!password) {
return alert("请输入密码");
}
setLoading(true);
try {
await actions.login(username, password);
setLoading(false);
alert("登录成功");
} catch (error) {
setLoading(false);
alert(`登录失败:${error.message}`);
}
};
const logout = () => {
dispatch({
type: DESTROY_SESSION
});
};
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
{loading ? <div className="loading">登录中……</div> : null}
{user ? (
<div className="user">
<div className="field">用户名:{user.username}</div>
<div className="field">过期时间:{`${expireTime}`}</div>
<div className="button" onClick={actions.logout}>
使用 actions.logout 退出登录
</div>
<div className="button" onClick={logout}>
使用 dispatch 退出登录
</div>
</div>
) : (
<div className="form">
<label className="field">
用户名:
<input
value={username}
onChange={e => setUsername(e.target.value)}
/>
</label>
<label className="field">
密码:
<input
value={password}
onChange={e => setPassword(e.target.value)}
type="password"
/>
</label>
<div className="button" onClick={login}>
登录
</div>
</div>
)}
</header>
</div>
);
}
export default App;
整个实现我们使用到了 React 的 useContext 共享上下文关系,这个是关系、 useEffect 用来实现 reducer 的 log , useReducer 实现 redux 里面的 combineReducer 功能,整体上来讲,实现还是足够绝大多数中小型项目使用的。
来自:https://pantao.parcmg.com/press/hooks-context-based-state-management-react-app.html
网页是用户与网站对接的入口,当我们允许用户在网页上进行一些频繁的操作时,对用户而言,误删、误操作是一件令人抓狂的事情,“如果时光可以倒流,这一切可以重来……”。
在React写应用的时候,难免遇到跨组件通信的问题。现在已经有很多的解决方案。React本身的Context,Redux结合React-redux,Mobx结合mobx-react
这个库的作者希望使用 React 内置 API ,直接实现状态管理的功能。看完这个库的说明后,没有想到代码可以这个玩。短短几行代码,仅仅使用 React Hooks ,就实现了状态管理的功能。
我们平时开发的大部分项目,由于复杂度不够, 很少使用 Vuex、Redux 等状态管理库,就算引入了 Vuex 这些库,也只是当作一个全局数据引用,并非对应用状态进行管理。但一旦页面的复杂度比较高,必然要引入状态管理,今天就聊聊我理解中的状态管理。
首先要明确为什么要使用redux,这一点很重要,如果不知道为什么使用redux,那么在开发的过程中肯定不能合理的使用redux.首先来看redux的本质:redux做为一款状态管理工具,主要是为了解决组件间通信的问题。
当我们使用编译器创建一个新Flutter应用的时候,我们可以在主界面看到两个小部件StatelessWidget和StatefulWidget。这是两个最常见使用最频繁的小部件了。StatelessWidget ,StatefulWidget
本文回答了以下问题:么是共享可变状态?为什么会出现问题?如何避免其问题?标有(高级)的部分会更深入,如果你想更快地阅读本文,可以跳过。
项目不大, 又不想用Vuex, 那么使用Observable来实现状态共享也不失为一个选择。用法 :让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象
当我们登录成功,在这个页面刷新,页面并没有保存登录状态;今天我们就来看一下如何在后台使用cookie保存用户登录状态。做到刷新页面仍然显示在用户登录界面。node实现保持登录状态的方法如下:
React组件内部的状态是在渲染之间保持不变的封装数据。useState()是React钩子,负责管理功能组件内部的状态。我喜欢useState()确实使状态处理变得非常容易。但是我经常遇到类似的问题:
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!