начало
Я думаю, что для проекта со сложной логикой особенно важно управление состоянием.Хорошо ли управление состоянием или нет, напрямую зависит от того, являются ли логика, удобочитаемость и ремонтопригодность проекта ясной, легко читаемой и эффективной.
От самых ранних компонентов класса, использующих this.state, this.setState для управления состоянием, до публикации и подписки на избыточность, подписку, отправку, использование избыточности сталкивается с повторяющимися и тяжелыми редюсерами, что делает меня инженером Ctrl CV. Так что снова на связиdva, который основан наreduxа такжеredux-sagaСхема потока данных. Разделите общее состояние по модели, используйте метод Connect для передачи состояния на необходимые глубокие компоненты.
После того, как позже появились реагирующие хуки, в отрасли также появилось много самоуправляемых состояний, основанных на инкапсуляции хуков, каждый модуль имеет хранилище состояний, основанное на его собственных хуках. Он действительно решает управление состоянием функциональных компонентов и управление состоянием внутри самого модуля, но все же не может решить проблему, заключающуюся в том, что в глобальном компоненте зависимость состояния послойной передачи делает структуру сложной и громоздкой. Как мы можем общаться между компонентами без каких-либо инструментов управления?
Почему бы нет?
Разве это не означает, что мы не используем инструменты управления, такие как dva? Я не говорю, что два — это плохо, но я не думаю, что иногда это необходимо. Я думаю, что он слишком тяжелый.
Что ты изучал?
Прочитав эту статью, даже если вы считаете, что мой стиль управления не очень хорош, вы сможете изучить и понять useMemo, useContext, useImmer и т. д.
react context
Context-ReactВведение на официальном сайте
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
createContext достигается в основном во время межкомпонентного взаимодействия
const MyContext = React.createContext(defaultValue);
<MyContext.Provider value={/* 某个值 */}>
<App>
... 多层组件嵌套内有一个 Goods 组件
<Goods />
</App>
</MyContext.Provider >
// 某个 子子子子子组件 Goods
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
Конкретные практические случаи
app.js
import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State 也包含了更新函数,因此它会被传递进 context provider。
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// 整个 state 都被传递进 provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
ReactDOM.render(<App />, document.root);
// Theme context,默认的 theme 是 “light” 值
const ThemeContext = React.createContext('light');
// 用户登录 context
const UserContext = React.createContext({
name: 'Guest',
});
class App extends React.Component {
render() {
const {signedInUser, theme} = this.props;
// 提供初始 context 值的 App 组件
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Layout />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Layout() {
return (
<div>
<Sidebar />
<Content />
</div>
);
}
// 一个组件可能会消费多个 context
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<ProfilePage user={user} theme={theme} />
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
Инкапсулируйте собственное межкомпонентное управление
./connect.js файл
Инкапсулировать метод подключения
Использование connect также основано на идее react-redux и инкапсулирует ее как метод. Вызов метода connect возвращает компонент более высокого порядка. И метод подключения поддерживает передачу функции для фильтрации и фильтрации состояния, требуемого подкомпонентами, что также легко поддерживать, повторно отображать и т. д.
import React, { createContext } from 'react';
import { useImmer } from 'use-immer';
// useImmer 文章末尾有介绍推荐
const ctx = createContext();
const { Consumer, Provider } = ctx
const useModel = (initialState) => {
const [state, setState] = useImmer(initialState);
return [
state,
setState
];
}
const createProvider = () => {
function WrapProvider(props) {
const { children, value } = props;
const [_state, _dispatch] = useModel(value)
return (
<Provider value={{
_state,
_dispatch,
}}>
{children}
</Provider>
)
}
return WrapProvider
}
export const connect = fn => ComponentUi => () => {
return (
<Consumer>
{
state => {
const {_state, _dispatch} = state
const selectState = typeof fn === 'function' ? fn(_state) : _state;
return <ComponentUi _state={selectState} _dispatch={_dispatch} />
}
}
</Consumer>
)
};
export default createProvider;
Как использовать
import React from 'react';
import Header from './layout/Header.jsx';
import Footer from './layout/Footer.jsx';
import createProvider from './connect';
const Provider = createProvider()
const initValue = { user: 'xiaoming', age: 12 }
function App() {
return (
<Provider value={initValue}>
<Header />
<Footer />
</Provider>
)
}
export default App;
Header.jsx
import React from 'react';
import { Select } from 'antd';
import { connect } from '../connect';
const { Option } = Select;
function Head({ _state: { user, age }, _dispatch }) {
return (
<div className="logo" >
<Select defaultValue={user} value={user} onChange={(value) => {
_dispatch(draft => {
draft.user = value
})
}}>
<Option value='xiaoming'>小明</Option>
<Option value='xiaohong'>小红</Option>
</Select>
<span>年龄{age}</span>
</div>
)
}
export default connect()(Head);
Footer.jsx
import React, { Fragment } from 'react';
import { Select } from 'antd';
import { connect } from '../../connect';
const { Option } = Select;
function Footer({ _state, _dispatch }) {
const { user, age } = _state;
return (
<Fragment>
<p style={{marginTop: 40}}>用户:{user}</p>
<p>年龄{age}</p>
<div>
<span>改变用户:</span>
<Select
defaultValue={user}
value={user}
onChange={(value) => {
_dispatch(draft => {
draft.user = value
})
}}>
<Option value='xiaoming'>小明</Option>
<Option value='xiaohong'>小红</Option>
</Select></div>
<div>
<span>改变年龄:</span>
<input onChange={(e) => {
// 这里使用 persist 原因可以看文章末尾推荐
e.persist();
_dispatch(draft => {
draft.age = e.target.value
})
}} />
</div>
</Fragment>
)
}
export default connect()(Footer);
использовать useContext
Мы все знаем, что useContext также выпущен после версии 16.8, поэтому мы можем оптимизировать метод подключения, используя useContext.
// 未使用 useContext
export const connect = (fn) => (ComponentUi) => () => {
const state = useContext(ctx)
console.log(state);
return (
<Consumer>
{
state => {
const { _state, _dispatch } = state
const selectState = typeof fn === 'function' ? fn(_state) : _state;
return <ComponentUi _state={selectState} _dispatch={_dispatch} />
}
}
</Consumer>
)
};
// 使用 useContext
export const connect = fn => ComponentUi => () => {
const { _state, _dispatch } = useContext(ctx);
const selectState = typeof fn === 'function' ? fn(_state) : _state;
return <ComponentUi _state={selectState} _dispatch={_dispatch} />;
};
Примечание: называетсяuseContext
Компонент всегда будет перерисовываться при изменении значения контекста. Если повторный рендеринг компонентов обходится дорого, вы можете узнать, как его оптимизировать, порекомендовав ненужный повторный рендеринг дорогостоящих компонентов в конце статьи.
наконец
Шаг 4 коды, чтобы запустить
git clone https://github.com/zouxiaomingya/blog
cd blog
npm i
npm start
Во всей статье, если есть какие-либо ошибки или неточности, обязательно исправьте их, спасибо!
Ссылаться на: