Hooks
Hooks
class
Hooks
class
-
componentDidMount
componentWillUnmount
componentDidMount
componentDidUpdate
useEffect
this
Hooks
react
react-dom
Hooks
React-Redux
connect
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
// action creators
import { queryFormData } from "@/data/queryFormData/action";
import { submitFormData } from "@/data/submitFormData/action";
function Form(props) {
const {
formId
formData,
queryFormData,
submitFormData,
} = props;
useEffect(() => {
// 请求表单数据
queryFormData(formId);
},
// 指定依赖,防止组件重新渲染时重复请求
[queryFormData, formId]
);
// 处理提交
const handleSubmit = usefieldValues => {
submitFormData(fieldValues);
}
return (
<FormUI
data={formData}
onSubmit={handleSubmit}
/>
)
}
function mapStateToProps(state) {
return {
formData: state.formData
};
}
function mapDispatchToProps(dispatch, ownProps) {
// withRouter传入的prop,用于编程式导航
const { history } = ownProps;
return {
queryFormData(formId) {
return dispatch(queryFormData(formId));
},
submitFormData(fieldValues) {
return dispatch(submitFormData(fieldValues))
.then(res) => {
// 提交成功则重定向到主页
history.push('/home');
};
}
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(React.memo(Form));
mapDispatchToProps
queryFormData
useEffect
mapDispatchToProps
submitFormData
history
mapStateToProps
formData
mapDispatchToProps
ownProps
function mapDispatchToProps(dispatch, ownProps) { // **问题在于这个ownProps!!!**
const { history } = ownProps;
...
}
withRouter
history
mapDispatchToProps
mapDispatchToProps
ownProps
mapDispatchTopProps
mapDispatchToProps
queryFormData
submitFormData
<Form/>
queryFormData
submitFormData
useEffect
// selectorFactory.js
...
// 此函数在connected组件接收到new props时会被调用
function handleNewProps() {
if (mapStateToProps.dependsOnOwnProps)
stateProps = mapStateToProps(state, ownProps)
// 声明mapDispatchToProps时如果使用了第二个参数(ownProps)这里会标记为true
if (mapDispatchToProps.dependsOnOwnProps)
// 重新调用mapDispatchToProps,更新dispatchProps
dispatchProps = mapDispatchToProps(dispatch, ownProps)
// mergeProps的做法其实是:mergedProps = { ...ownProps, ...stateProps, ...dispatchProps }
// 最后传入被connect包裹的组件
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
...
function Form(props) {
const {
formId,
queryFormData,
...
} = props;
useEffect(() => {
// 请求表单数据
queryFormData(formId);
},
// 传入空数组,起到类似componentDidMount的效果
[]
);
...
}
useEffect
useEffect
componentDidMount
queryFormData
formId
Hooks FAQeslint-plugin-react-hooks
ownProps
function Form(props) {
const {
formId
queryFormData,
submitFormData,
history
...
} = props;
useEffect(() => {
queryFormData(formId);
},
// 由于声明mapDispatchToProps时没使用ownProps,所以queryFormData是稳定的
[queryFormData, formId]
);
const handleSubmit = fieldValues => {
submitFormData(fieldValues)
// 把需要用到ownProps的逻辑迁移到组件内定义(使用了redux-thunk中间件,返回Promise)
.then(res => {
history.push('/home');
});
}
...
}
...
function mapDispatchToProps(dispatch) { // 不再声明ownProps参数
return {
queryFormData(formId) {
return dispatch(queryFormData(formId));
},
submitFormData(fieldValues) {
return dispatch(submitFormData(fieldValues));
}
}
}
...
mapDispatchToProps
mapDispatchToProps
ownProps
mapStateToProps
mapDispatchToProps
connect
mapDispatchToProps
dispatch
dispatch
...
// action creators
import { queryFormData } from "@/data/queryFormData/action";
import { submitFormData } from "@/data/submitFormData/action";
function Form(props) {
const {
formId
history,
dispatch
...
} = props;
useEffect(() => {
// 在组件内使用dispatch
// 注意这里的queryFormData来自import,而非props,不会变,所以不用写进依赖数组
dispatch(queryFormData(formId))
},
[dispatch, formId]
);
const handleSubmit = fieldValues => {
// 在组件内使用dispatch
dispatch(submitFormData(fieldValues))
.then(res => {
history.push('/home');
});
}
...
}
...
// 不传入mapDispatchToProps
export default withRouter(connect(mapStateToProps, null)(React.memo(Form));
useEffect
useCallback
import { actionCreator1 } from "@/data/actionCreator1/action";
import { actionCreator2 } from "@/data/actionCreator2/action";
import { actionCreator3 } from "@/data/actionCreator3/action";
...
function Form(props) {
const {
dep1,
dep2,
dep3,
dispatch
...
} = props;
// 利用useCallback把useEffect要使用的函数抽离到外部
const fetchUrl1() = useCallback(() => {
dispatch(actionCreator1(dep1));
.then(res => {...})
.catch(err => {...});
}, [dispatch, dep1]); // useCallback的第二个参数跟useEffect一样,是依赖项
const fetchUrl2() = useCallback(() => {
dispatch(actionCreator2(dep2));
.then(res => {...})
.catch(err => {...});
}, [dispatch, dep2]);
const fetchUrl3() = useCallback(() => {
dispatch(actionCreator3(dep3));
.then(res => {...})
.catch(err => {...});
}, [dispatch, dep3]);
useEffect(() => {
fetchUrl1();
fetchUrl2();
fetchUrl3();
},
// useEffect的直接依赖变成了useCallback包裹的函数
[fetchUrl1, fetchUrl2, fetchUrl3]
);
// 为了避免子组件发生不必要的re-render,handleSubmit其实也应该用useCallback包裹
const handleSubmit = useCallback(fieldValues => {
// 在组件内使用dispatch
dispatch(submitFormData(fieldValues))
.then(res => {
history.push('/home');
});
});
return (
<FormUI
data={formData}
onSubmit={handleSubmit}
/>
)
}
...
useCallback
useEffect
useCallback
useEffect
mapStateToProps
ownProps
mapStateToProps
// 此函数在connected组件接收到new props时会被调用
function handleNewProps() {
// 声明mapStateToProps时如果使用了ownProps参数同样会产生新的stateProps!
if (mapStateToProps.dependsOnOwnProps)
stateProps = mapStateToProps(state, ownProps)
if (mapDispatchToProps.dependsOnOwnProps)
dispatchProps = mapDispatchToProps(dispatch, ownProps)
mergedProps = mergeProps(stateProps, dispatchProps, ownProps)
return mergedProps
}
useEffect
import { useSelector, useDispatch } from 'react-redux'
// selector函数的用法和mapStateToProps相似,其返回值会作为useSelector的返回值,但与mapStateToProps不同的是,前者可以返回任何类型的值(而不止是一个对象),此外没有第二个参数ownProps(因为可以在组件内通过闭包拿到)
const result : any = useSelector(selector : Function, equalityFn? : Function)
const dispatch = useDispatch()
...
import { useSelector, useDispatch } from "react-redux";
// action creators
import { queryFormData } from "@/data/queryFormData/action";
import { submitFormData } from "@/data/submitFormData/action";
function Form(props) {
const {
formId
history,
dispatch
...
} = props;
const dispatch = useDispatch();
useEffect(() => {
dispatch(queryFormData(formId))
},
[dispatch, formId]
);
const handleSubmit = useCallback(fieldValues => {
dispatch(submitFormData(fieldValues))
.then(res => {
history.push('/home');
});
}, [dispatch, history]);
const formData = useSelector(state => state.formData;);
...
return (
<FormUI
data={formData}
onSubmit={handleSubmit}
/>
);
}
...
// 无需使用connect
export default withRouter(React.memo(Form));
"不使用mapDispatchToProps"
dispatch
dispatch
state
mapStateToProps
useSelector
useSelector
===
useSelector
reselectuseSelector
shallowEqual
import { useSelector, shallowEqual } from 'react-redux'
const selector = state => ({
a: state.a,
b: state.b
});
const data = useSelector(selector, shallowEqual);
Hooks
Hooks
-
Hooks
useContext
useReducer
-
import { createContext, useContext, useReducer, memo } from 'react'; function reducer(state, action) { switch (action.type) { case 'UPDATE_HEADER_COLOR': return { ...state, headerColor: 'yellow' }; case 'UPDATE_CONTENT_COLOR': return { ...state, contentColor: 'green' }; default: break; } } // 创建一个context const Store = createContext(null); // 作为全局state const initState = { headerColor: 'red', contentColor: 'blue' }; const App = () => { const [state, dispatch] = useReducer(reducer, initState); // 在根结点注入全局state和dispatch方法 return ( <Store.Provider value={{ state, dispatch }}> <Header/> <Content/> </Store.Provider> ); }; const Header = memo(() => { // 拿到注入的全局state和dispatch const { state, dispatch } = useContext(Store); return ( <header style={{backgroundColor: state.headerColor}} onClick={() => dispatch('UPDATE_HEADER_COLOR')} /> ); }); const Content = memo(() => { const { state, dispatch } = useContext(Store); return ( <div style={{backgroundColor: state.contentColor}} onClick={() => dispatch('UPDATE_CONTENT_COLOR')} /> ); });
context
state
actions
dispatch
Provider
<Header/>
<Content/>
<Header/>
<Content/>
memo
memo
state
useContext
memo
-
context
-
memo
const Header = () => { const { state, dispatch } = useContext(Store); return memo(<ThemedHeader theme={state.headerColor} dispatch={dispatch} />); }; const ThemedHeader = memo(({theme, dispatch}) => { return ( <header style={{backgroundColor: theme}} onClick={() => dispatch('UPDATE_HEADER_COLOR')} /> ); });
-
useMemo
hookconst Header = () => { const { state, dispatch } = useContext(Store); return useMemo( () => ( <header style={{backgroundColor: state.headerColor}} onClick={() => dispatch('UPDATE_HEADER_COLOR')} /> ), [state.headerColor, dispatch] ); };
connect
ownProps