如何在不重新渲染所有子组件的情况下更新 material-bottom-tabs 徽章文本?
How to update material-bottom-tabs badge text without re-rendering all child components?
我已经使用 createMaterialBottomTabNavigator
和 redux 来更改徽章文本。这是代码:
class Router extends React.PureComponent {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount() {
this.props.appAct()
this.props.getCartAct()
}
HomeStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
CartStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Cart" component={CartScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
render() {
let { internet, showIntro } = this.props;
if (!internet) return <NoInternetScreen />
if (showIntro) return <AppIntro />
return (
<NavigationContainer ref={navigationRef}>
<Tab.Navigator
style={{ backgroundColor: "transparent", overflow: "hidden" }}
initialRouteName="Home"
activeColor="#ac85f2"
inactiveColor={Colors.dark50}
barStyle={{
borderColor: Colors.dark70,
overflow: 'hidden',
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
backgroundColor: "#eee"
}}
>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarLabel: <Text default> home </Text>,
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Cart"
component={CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> cart </Text>,
tabBarBadge: props.cart.length, // <= here it changes the badge text
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="cart-outline" color={color} size={26} />
),
})
}
/>
</Tab.Navigator>
</NavigationContainer>
)
}
}
function mapStateToProps(state) {
return {
internet: state.appReducer.internet,
showIntro: state.appReducer.showIntro,
cart: state.appReducer.cart,
}
}
function mapDispatchToProps(dispatch) {
return {
appAct: () => dispatch(appAction()),
getCartAct: () => dispatch(getCartAction()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Router)
问题是当我尝试从一个动作中更改购物车道具时,整个组件重新呈现。例如,这是我的 HomeScreen
组件,它在 tabBarBadge
更改时重新呈现:
import _ from 'lodash';
import React from 'react';
import { StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
import { Constants, Spacings, View, Text, Carousel, Colors, Button, Card } from 'react-native-ui-lib';
import { connect } from 'react-redux'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { homeAction } from '../actions'
import Loaders from '../components/loaders'
import { HomeCats } from '../components/HomeCats';
const INITIAL_PAGE = 2;
const IMAGES = [
require('../assets/images/s1.jpg'),
require('../assets/images/s2.jpg'),
require('../assets/images/s3.jpg'),
];
const image = require("../assets/images/b2.jpg")
class HomeScreen extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
orientation: Constants.orientation,
width: this.getWidth(),
limitShownPages: false,
numberOfPagesShown: 7,
currentPage: INITIAL_PAGE,
autoplay: false
}
}
componentDidMount() {
console.log("componentDidMount")
console.log('this.props.categories.length', this.props.categories.length)
if (this.props.categories.length < 1) {
this.props.getCategoriesAct()
}
}
getWidth = () => {
return Constants.windowWidth - Spacings.s5 * 2;
}
onChangePage = (currentPage) => {
this.setState({ currentPage });
}
onPagePress = (index) => {
this.carousel.goToPage(index, true);
}
render() {
const { limitShownPages, numberOfPagesShown, autoplay, width } = this.state;
const { loadingCats, categories } = this.props;
return (
<ScrollView showsVerticalScrollIndicator={false} >
<View marginV-20 >
<View marginB-15>
<Carousel
key={numberOfPagesShown}
migrate
ref={(r) => (this.carousel = r)}
//loop
autoplay={autoplay}
onChangePage={this.onChangePage}
pageWidth={width}
itemSpacings={Spacings.s3}
containerMarginHorizontal={Spacings.s2}
initialPage={INITIAL_PAGE}
containerStyle={{ height: 180, }}
containerPaddingVertical={18}
pageControlPosition={'over'}
pageControlProps={{ onPagePress: this.onPagePress, limitShownPages }}
allowAccessibleLayout
>
{
IMAGES.map((image, i) =>
<View flex centerV br20 key={i} style={{
shadowColor: "#eee",
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.09,
shadowRadius: 1,
elevation: 15,
}}>
<Image
imageStyle="contain"
style={{ flex: 1, borderRadius: 20, width: "100%" }}
source={image}
/>
</View>
)
}
</Carousel>
</View>
</View>
</ScrollView>
)
}
}
function mapStateToProps(state) {
return {
error: state.homeReducer.error,
loadingCats: state.categoryReducer.loading,
categories: state.categoryReducer.categories,
}
}
function mapDispatchToProps(dispatch) {
return {
homeAct: () => dispatch(homeAction()),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen)
有没有什么方法可以在不重新渲染其他组件的情况下更改 tabBarBadge
道具?
终于在我的头撞墙之后找到了答案:
将图标和标题移动到新组件:
export const CartTabnavigator = (props) => {
const dispatch = useDispatch()
const color = props.color
useEffect(() => {
console.log("props.cart", props.cart)
dispatch(getCartAction())
}, [])
return (
<View>
<MaterialCommunityIcons name="cart-outline" color={color} size={26} style={{
transform: [
{ scaleX: -1 }
]
}} />
<View style={{ position: 'absolute', right: -5, top: -5, backgroundColor: Colors.red20, borderRadius: 9, width: 15, height: 15, justifyContent: 'center', alignItems: 'center' }}>
<Text white style={{ fontSize: 10, fontFamily: config.font }} >{props.cart.length}</Text>
</View>
</View>
);
}
const mapStateToProps = state => ({
cart: state.cartReducer.cart,
})
function mapDispatchToProps() {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(CartTabnavigator)
并在导航器中使用它,例如:
<Tab.Screen
name="Cart"
component={this.CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> سبد خرید </Text>,
tabBarIcon: ({ color }) => (
<CartTabNavigator color={color} />
),
})
}
/>
我已经使用 createMaterialBottomTabNavigator
和 redux 来更改徽章文本。这是代码:
class Router extends React.PureComponent {
constructor(props) {
super(props);
this.state = {}
}
componentDidMount() {
this.props.appAct()
this.props.getCartAct()
}
HomeStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
CartStack = () => (
<Stack.Navigator headerMode={'none'}>
<Stack.Screen name="Cart" component={CartScreen} />
<Stack.Screen name="Product" component={ProductStack} />
</Stack.Navigator>
)
render() {
let { internet, showIntro } = this.props;
if (!internet) return <NoInternetScreen />
if (showIntro) return <AppIntro />
return (
<NavigationContainer ref={navigationRef}>
<Tab.Navigator
style={{ backgroundColor: "transparent", overflow: "hidden" }}
initialRouteName="Home"
activeColor="#ac85f2"
inactiveColor={Colors.dark50}
barStyle={{
borderColor: Colors.dark70,
overflow: 'hidden',
borderTopWidth: 1,
borderLeftWidth: 1,
borderRightWidth: 1,
backgroundColor: "#eee"
}}
>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarLabel: <Text default> home </Text>,
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Cart"
component={CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> cart </Text>,
tabBarBadge: props.cart.length, // <= here it changes the badge text
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="cart-outline" color={color} size={26} />
),
})
}
/>
</Tab.Navigator>
</NavigationContainer>
)
}
}
function mapStateToProps(state) {
return {
internet: state.appReducer.internet,
showIntro: state.appReducer.showIntro,
cart: state.appReducer.cart,
}
}
function mapDispatchToProps(dispatch) {
return {
appAct: () => dispatch(appAction()),
getCartAct: () => dispatch(getCartAction()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Router)
问题是当我尝试从一个动作中更改购物车道具时,整个组件重新呈现。例如,这是我的 HomeScreen
组件,它在 tabBarBadge
更改时重新呈现:
import _ from 'lodash';
import React from 'react';
import { StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
import { Constants, Spacings, View, Text, Carousel, Colors, Button, Card } from 'react-native-ui-lib';
import { connect } from 'react-redux'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import { homeAction } from '../actions'
import Loaders from '../components/loaders'
import { HomeCats } from '../components/HomeCats';
const INITIAL_PAGE = 2;
const IMAGES = [
require('../assets/images/s1.jpg'),
require('../assets/images/s2.jpg'),
require('../assets/images/s3.jpg'),
];
const image = require("../assets/images/b2.jpg")
class HomeScreen extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
orientation: Constants.orientation,
width: this.getWidth(),
limitShownPages: false,
numberOfPagesShown: 7,
currentPage: INITIAL_PAGE,
autoplay: false
}
}
componentDidMount() {
console.log("componentDidMount")
console.log('this.props.categories.length', this.props.categories.length)
if (this.props.categories.length < 1) {
this.props.getCategoriesAct()
}
}
getWidth = () => {
return Constants.windowWidth - Spacings.s5 * 2;
}
onChangePage = (currentPage) => {
this.setState({ currentPage });
}
onPagePress = (index) => {
this.carousel.goToPage(index, true);
}
render() {
const { limitShownPages, numberOfPagesShown, autoplay, width } = this.state;
const { loadingCats, categories } = this.props;
return (
<ScrollView showsVerticalScrollIndicator={false} >
<View marginV-20 >
<View marginB-15>
<Carousel
key={numberOfPagesShown}
migrate
ref={(r) => (this.carousel = r)}
//loop
autoplay={autoplay}
onChangePage={this.onChangePage}
pageWidth={width}
itemSpacings={Spacings.s3}
containerMarginHorizontal={Spacings.s2}
initialPage={INITIAL_PAGE}
containerStyle={{ height: 180, }}
containerPaddingVertical={18}
pageControlPosition={'over'}
pageControlProps={{ onPagePress: this.onPagePress, limitShownPages }}
allowAccessibleLayout
>
{
IMAGES.map((image, i) =>
<View flex centerV br20 key={i} style={{
shadowColor: "#eee",
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.09,
shadowRadius: 1,
elevation: 15,
}}>
<Image
imageStyle="contain"
style={{ flex: 1, borderRadius: 20, width: "100%" }}
source={image}
/>
</View>
)
}
</Carousel>
</View>
</View>
</ScrollView>
)
}
}
function mapStateToProps(state) {
return {
error: state.homeReducer.error,
loadingCats: state.categoryReducer.loading,
categories: state.categoryReducer.categories,
}
}
function mapDispatchToProps(dispatch) {
return {
homeAct: () => dispatch(homeAction()),
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen)
有没有什么方法可以在不重新渲染其他组件的情况下更改 tabBarBadge
道具?
终于在我的头撞墙之后找到了答案: 将图标和标题移动到新组件:
export const CartTabnavigator = (props) => {
const dispatch = useDispatch()
const color = props.color
useEffect(() => {
console.log("props.cart", props.cart)
dispatch(getCartAction())
}, [])
return (
<View>
<MaterialCommunityIcons name="cart-outline" color={color} size={26} style={{
transform: [
{ scaleX: -1 }
]
}} />
<View style={{ position: 'absolute', right: -5, top: -5, backgroundColor: Colors.red20, borderRadius: 9, width: 15, height: 15, justifyContent: 'center', alignItems: 'center' }}>
<Text white style={{ fontSize: 10, fontFamily: config.font }} >{props.cart.length}</Text>
</View>
</View>
);
}
const mapStateToProps = state => ({
cart: state.cartReducer.cart,
})
function mapDispatchToProps() {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(CartTabnavigator)
并在导航器中使用它,例如:
<Tab.Screen
name="Cart"
component={this.CartStack}
options={({ route }) =>
({
tabBarLabel: <Text default> سبد خرید </Text>,
tabBarIcon: ({ color }) => (
<CartTabNavigator color={color} />
),
})
}
/>