react-china.org/t/topic/340… React-Redux 官方 Hooks 文档说明
使用useSelector useDispatch 替代connect cloud.tencent.com/developer/a…
使用 React Hooks 代替 Redux zhuanlan.zhihu.com/p/66020264
Hooks注入mapStateToProps/mapPopularDispatchToProps属性
import { connect } from 'react-redux';
const mapStateToProps = state => ({
myProfile: state.apps.myProfile,
});
const mapPopularDispatchToProps = dispatch => ({
onOpenChat: ({
no
}) =>
dispatch(actions.onOpenChat({
no
})),
});
export default connect(
mapStateToProps,
mapPopularDispatchToProps,
)(activiteListCom);
//获取
const activiteListCom = ((props) => {
const { myProfile, onOpenChat } = props;
//-----------------------------------------------
//新的api里直接这样就可以拿的到
import {useSelector} from 'react-redux';
//组件里
...
const state = useSelector(state => {
return {
chatBadgeCount: state.chat.chatBadgeCount,
letterBadgeCount: state.sideMenu.letterBadgeCount,
};
});
//使用
const {myProfile, personal} = state;
export default CustomerService;
发送事件useDispatch
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
useStore()
const store = useStore()
这个 hook 返回传递给 组件的 Redux sotore 的引用。
这个 hook 也许不应该被经常使用。 你应该将 useSelector() 作为你的首选。但是,在一些不常见的场景下,你需要访问 store,这个还是有用的,比如替换 store 的 reducers。
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// EXAMPLE ONLY! Do not do this in a real app.
// The component will not automatically update if the store state changes
return <div>{store.getState()}</div>
}
自定义 context
组件允许你通过 context 参数指定一个可选的 context。在你构建复杂的可复用的组件时,你不想让你自己的私人 store 与使用这个组件的用户的 Redux store 发生冲突,这个功能是很有用的,
通过使用 hook creator 函数来创建自定义 hook,从而访问可选的 context。
import React from 'react'
import {
Provider,
createStoreHook,
createDispatchHook,
createSelectorHook
} from 'react-redux'
const MyContext = React.createContext(null)
// Export your custom hooks if you wish to use them in other files.
export const useStore = createStoreHook(MyContext)
export const useDispatch = createDispatchHook(MyContext)
export const useSelector = createSelectorHook(MyContext)
const myStore = createStore(rootReducer)
export function MyProvider({ children }) {
return (
<Provider context={MyContext} store={myStore}>
{children}
</Provider>
)
}
性能
正如上文提到的,在一个 action 被分发(dispatch) 后,useSelector() 默认对 select 函数的返回值进行引用比较 ===,并且仅在返回值改变时触发重渲染。但是,不同于 connect(),useSelector()并不会阻止父组件重渲染导致的子组件重渲染的行为,即使组件的 props 没有发生改变。
如果你想要类似的更进一步的优化,你也许需要考虑将你的函数组件包裹在 React.memo() 中:
const CounterComponent = ({ name }) => {
const counter = useSelector(state => state.counter)
return (
<div>
{name}: {counter}
</div>
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)
```
```
/**
* Created by Webstorm
* User: King
* Date: 2020-02-11
* Time: 10:03
*/
import React, { useRef, useState } from 'react';
import { BlockCom, TextCom, CaveatAlertCom } from '@base';
import { Image, TouchableOpacity } from 'react-native';
import { PullListCom } from '@business';
import { IMG_URL } from '@apis/config';
import { getImageUtils, NavigationUtil } from '@utils';
import { StatusBadgeCom } from '@business';
import moment from 'moment';
import actions from '@actions';
import { connect } from 'react-redux';
import { ToastUtil } from '@service/Toast';
import I18n from '@i18n';
const activiteListCom = ((props) => {
const { setListData, canDelet, type, deletePrompt, onPostWatch, chatContentData, myProfile, onOpenChat, emptyTip } = props;
const [getDeletFlag] = useState(canDelet); // 是否存在删除功能
const caveatAlertRef = useRef();
const pullRef = useRef();
/**
* 跳转页面
* @method goPage
* @param type {String} 区分聊天还是信件类型
* @param femaleId {String} 女士ID
* @param online {Boolean} 女士是否在线
* @param engName {String} 女士名字
* @param iconPath {String} 女士头像
* @param no {String} 女士编号
* @return {Any}
*/
const goPage = async ({ type, femaleId, online, engName, iconPath, no }) => {
switch (type) {
case 'chat':
await onOpenChat({
maleId: myProfile.id,
femaleId,
chatContentData,
engName,
iconPath,
online,
no
});
break;
case 'email':
NavigationUtil.navigate('WirteLetter', {
type: 'reply',
icon: { senderIcon: iconPath },
name: engName,
id: femaleId,
});
break;
}
};
/**
* 處理按鈕
* @method _renderButton
* @param type {String} 区分聊天还是信件类型
* @param item {Object} 女士信息
* @return {Any}
*/
const _renderButton = ({ type, item }) => {
const { femaleId, femaleName, femaleIconPath, onlineState, no } = item;
// 聊天参数
const _chatParams = {
femaleId,
engName: femaleName,
iconPath: femaleIconPath,
online: onlineState,
no
};
if (!type) {
return null;
}
const _keys = {
'chat': {
imgW: 18,
imgH: 18,
imgPath: getImageUtils.poke_me_chat,
},
'email': {
imgW: 18,
imgH: 14,
imgPath: getImageUtils.poke_me_mail,
},
};
return <TouchableOpacity
onPress={() => goPage({
type,
..._chatParams,
})}>
<BlockCom
flex={false}
width={77}
height={30}
style={{
backgroundColor: '#ffebe4',
marginRight: $getScreen.px2dp(10),
}}
radius={8}
center
middle
>
<Image source={_keys[type].imgPath}
style={{
width: $getScreen.px2dp(_keys[type].imgW),
height: $getScreen.px2dp(_keys[type].imgH),
}}/>
</BlockCom>
</TouchableOpacity>;
};
/**
* 删除我喜欢的女士
* @method deletList
* @param {String} femaleId 当前女士id
* @param {String} id 当前列表id
* @param {String} no 女生編號
* @param {Number} index 当前女士列表索引
* @return {any}
*/
const deletList = async ({ femaleId, id, no, index }) => {
caveatAlertRef.current.show({
title: deletePrompt,
onConfirm: () => {
const { onDeletList } = props;
$showLoading(true);
onDeletList && onDeletList({
femaleId,
type,
id,
no,
}, () => {
$showLoading(false);
pullRef.current.deleteItem(index);
ToastUtil.show(I18n.t('common.success'));
});
},
});
};
/**
* 渲染女士列表
* @method renderItem
* @param {Object} item 对象信息
* @param {Number} index 索引
* @return {any}
*/
const renderItem = ({ item, index }) => {
const {
id,
no,
femaleName,
femaleId,
femaleAge,
femaleIconPath,
onlineState,
date,
country,
city,
} = item;
return <TouchableOpacity onPress={() => NavigationUtil.navigate('LadyDetails', { no: no })}>
<BlockCom
row
height={103}
flex={false}
bgColor={'white'}
padding={[12, 15]}>
{/*头像描绘*/}
<BlockCom
flex={false}
marginRight={11}>
<Image
source={femaleIconPath ? { uri: IMG_URL + femaleIconPath } : getImageUtils.femaleHead}
style={$styles.radius(79)}/>
<BlockCom style={{
position: 'absolute',
right: 0,
bottom: 0,
}}>
<StatusBadgeCom
isOnline={onlineState}
/>
</BlockCom>
</BlockCom>
{/*信息层描绘*/}
<BlockCom between style={$styles.borB} paddingBottom={13} marginBottom={-12}>
<BlockCom row flex={false}>
<BlockCom row center>
<BlockCom flex={false}
maxWidth={'50%'}
><TextCom ellipsis>{femaleName}</TextCom>
</BlockCom>
<BlockCom flex={false}
maxWidth={'50%'}
><TextCom ellipsis size={'m'}
color={'lightGrey'}>,{femaleAge}</TextCom>
</BlockCom>
</BlockCom>
<BlockCom bottom row>
<TextCom size={'m'} color={'lightGrey'}
style={{ marginRight: $getScreen.px2dp(10) }}>{moment(date)
.format('YYYY-MM-DD')}</TextCom>
{getDeletFlag ?
<TouchableOpacity
hitSlop={{
left: 100,
right: 100,
top: 100,
bottom: 100
}}
onPress={() => deletList({
femaleId,
id,
no,
index,
})}>
<Image source={getImageUtils.like_photo_delet}
style={{
...$styles.wh(15),
}}/>
</TouchableOpacity> : null}
</BlockCom>
</BlockCom>
<BlockCom row center flex={false}>
<Image source={getImageUtils.poke_me_positioning}
style={{
width: $getScreen.px2dp(8),
height: $getScreen.px2dp(10),
marginRight: $getScreen.px2dp(2),
}}/>
<TextCom>{city},{country}</TextCom>
</BlockCom>
<BlockCom flex={false} row>
{_renderButton({
type: 'chat',
item,
})}
{_renderButton({
type: 'email',
item,
})}
</BlockCom>
</BlockCom>
</BlockCom>
</TouchableOpacity>;
};
//刷新后,通知父页面更新未读图标
const _onFetch = async (res, params) => {
console.log('通知父页面');
const result = await setListData(params);
onPostWatch && onPostWatch(); // 通知父页面请求完成
res(result[0]);
};
const ListEmptyComponent = () => {
return <BlockCom flex={false} height={60}><TextCom>Loding</TextCom></BlockCom>;
};
/**
* 渲染整体列表
* @method _renderList
* @return {any}
*/
const _renderList = (() => {
return <PullListCom
ref={pullRef}
renderItem={renderItem}
onFetch={_onFetch}
emptyTip={emptyTip}
emptyHeight={158}
/>;
})();
return (
<BlockCom>
<CaveatAlertCom ref={caveatAlertRef}/>
{_renderList}
</BlockCom>
);
});
const mapStateToProps = state => ({
myProfile: state.apps.myProfile,
chatContentData: state.chat.chatContentData,
});
const mapPopularDispatchToProps = dispatch => ({
onOpenChat: ({
no,
maleId,
femaleId,
chatContentData,
engName,
iconPath,
online
}) =>
dispatch(actions.onOpenChat({
no,
maleId,
femaleId,
chatContentData,
engName,
iconPath,
online
})),
});
export default connect(
mapStateToProps,
mapPopularDispatchToProps,
)(activiteListCom);
```