当我们使用
redux
进行数据管理的时候,一般都是在根组件通过Provider
的方式引入store
,然后在每个子组件中,通过connect
的方式使用高阶组件进行连接,这样造成的一个问题是,大量的高阶组件代码冗余度特别高,既然hooks
带来了新特性,不如一起来用用看
├── otherPage // 其他页面
| ├── index.jsx // 共享`Test`页面的状态;
├── Test // 测试页面
| ├── child.jsx // 测试页面的子组件。1、`useContext`定义的位置,获取父组件提供的`context`;2、`useEffect`进行异步请求;
| ├── index.jsx // 测试页面父组件。1、通过使用`Provider`提供给子组件`context`;2、`useReducer`定义的位置,引入一个`reducer`并且提供初始状态`initialState`;
| ├── otherPage.jsx // 其他页面,已删除~~
| └── reducer.jsx // 处理不同类型的`action`操作
Test/reducer.jsx
import axios from 'axios';
function reducer (state, action) {
switch (action.type) {
case 'ADD': // 加
return Object.assign({}, state, {
type: 'add',
index: ++state.index
});
case 'DOWN': // 减
return Object.assign({}, state, {
type: 'down',
index: --state.index
});
case 'FETCH': //请求
axios('/addFetch').then((result) => {
console.log(result);
}).catch((err) => {
console.log(err);
}); ;
return Object.assign({}, state);
default: // 重置
return Object.assign({}, state, {
index: 1
});
}
};
export default reducer;
Test/child.jsx
import React, { useContext, useEffect } from 'react';
import { FetContext } from './index';
import { Button } from 'antd-mobile';
function DeepChild (props) {
// If we want to perform an action, we can get dispatch from context.
const dispatch = useContext(FetContext);
function handleClick () {
dispatch({ type: 'ADD' });
}
const fetch = () => {
console.log('fetch');
dispatch({ type: 'FETCH' });
};
useEffect(() => {
console.log('child useEffect', props);
});
return (
<div>
<Button onClick={handleClick} type='primary'>Child Add</Button>
<br />
<Button onClick={fetch} type='primary'>Child Request</Button>
<br />
</div>
);
}
export default DeepChild;
Test/index.jsx
/* eslint-disable react/prop-types */
import React, { useReducer, useEffect } from 'react';
import reducer from './reducer';
import DeepChild from './child';
import { Button, InputItem } from 'antd-mobile';
export const FetContext = React.createContext(null);
function Test (props) {
const [state, dispatch] = useReducer(reducer, {
isFetching: false,
index: props.location.state.index || 1
});
useEffect(() => {
// ...
});
const fetch = () => {
dispatch({ type: 'FETCH' });
};
const confirmClick = () => {
dispatch({ type: 'DOWN' });
};
const goOtherPage = () => {
props.history.push({
pathname: 'otherPage',
state: {
index: state.index
}
});
};
const reset = () => {
dispatch({ type: 'RESET' });
};
return (
<FetContext.Provider value={dispatch}>
<InputItem value={state.index} />
<DeepChild {...state} />
<Button onClick={confirmClick} type='warning'>Parent Reduce</Button>
<br />
<Button type='warning' onClick={fetch}>Parent Request!</Button>
<br />
<Button type='primary' onClick={reset}>Reset Index</Button>
<br />
<Button type='ghost' onClick={goOtherPage}>Next Page</Button>
</FetContext.Provider>
);
}
export default Test;
OtherPage/index.jsx
/* eslint-disable react/prop-types */
import React, { useReducer, useEffect } from 'react';
import reducer from '../Test/reducer';
import { InputItem, Button } from 'antd-mobile';
function OtherPage (props) {
const [state, dispatch] = useReducer(reducer, props.location.state);
useEffect(() => {
console.log('OtherPage props', state);
});
const add = () => {
dispatch({ type: 'ADD' });
};
const goBack = () => {
console.log('123');
console.log('props', props);
// props.history.go(-1);
props.history.replace({
pathname: 'Test',
state: {
index: state.index
}
});
};
return (
<div>
<InputItem value={state.index} />
<Button onClick={add} type='primary'> add</Button>
<br />
<Button onClick={goBack} type='ghost'> Go Back</Button>
</div>
);
}
export default OtherPage;
useEffect()
可以看做是class
写法的componentDidMount
、componentDidUpdate
以及componentWillUnMount
三个钩子函数的组合。
compnentWillUnMount
生命周期调用useEffect
的第一个参数会在每次(包含第一次)数据更新时重新调用useEffect()
传入了第二个参数(数组类型)的时候,effect
函数会在第一次渲染时调用,其余仅当数组中的任一元素发生改变时才会调用。这相当于我们控制了组件的update
生命周期useEffect()
第二个数组为空则意味着仅在componentDidMount
周期执行一次