如何在不重新渲染所有子组件的情况下更新 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} />
          ),
         })
    }
 />