FairyQueen
FairyQueen

Reputation: 2373

Why are my items not being sorted (re-rendered) in React?

I have a button that when clicked calls a function that sorts the products by case amount. I am updating the products array so I assumed this would trigger a re-render of the products being mapped in the code below but it is not. Does anyone know how to get this to trigger the products.map to be re-rendered again thus displaying the new sorted products?

render() {
    const {products} = this.props;
    const cartIcon = (<Icon name="shopping-cart"  style={styles.cartIcon} />);

    sortCartItems = () => {
        products.sort((a, b) => a.cases > b.cases);
    }

    return (            
        <View style={styles.itemsInCartContent}>
            <View style={styles.cartHeader}>
                <TouchableOpacity onPress={sortCartItems}>
                    {cartIcon}
                    <Text>Sort</Text>
                </TouchableOpacity>
            </View>
            {products.map((product, index) =>
                <CartProductItem
                    ref="childCartProductItem"
                    key={product.id}
                    product={product}
                    index={index}
                    activeIndex={this.state.active}
                    triggerParentUpdate={() => this.collapseAllOtherProducts}
                />
            )}
        </View>
    );
}

Upvotes: 1

Views: 1561

Answers (2)

trixn
trixn

Reputation: 16309

A component should not mutate it's own props. If your data changes during the lifetime of a component you need to use state.

Your inline arrow function sortCartItems tries to mutate the products that come from props. Your need to store the products in the components state instead and call setState to change them which will trigger a re-render.

class MyComponent extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            products: props.products,
        }
    }

    sortCartItems = () => {
        this.setState(prevState => ({products: prevState.products.sort((a, b) => a.cases > b.cases);}))
    }

    render() {...}
}

Note that you need to use a callback in setState whenever you are updating the state based on the previous state. The callback receives the old state as a parameter and returns the new state.

Upvotes: 2

FairyQueen
FairyQueen

Reputation: 2373

I used a combination of messerbill's and trixn's answers to come up with the following which is now working. And I added a products property to state which receives its data from props.products

render() {
    const cartIcon = (<Icon name="shopping-cart"  style={styles.cartIcon} />);

    sortCartItems = () => {
        this.setState({
            products: this.state.products.sort((a, b) => a.cases > b.cases)
        });
    }

    return (            
        <View style={styles.itemsInCartContent}>
            <View style={styles.cartHeader}>
                <TouchableOpacity onPress={sortCartItems}>
                    {cartIcon}
                    <Text>Sort</Text>
                </TouchableOpacity>
            </View>
            {this.state.products.map((product, index) =>
                <CartProductItem
                    ref="childCartProductItem"
                    key={product.id}
                    product={product}
                    index={index}
                    activeIndex={this.state.active}
                    triggerParentUpdate={() => this.collapseAllOtherProducts}
                />
            )}
        </View>
    );
}

Upvotes: 0

Related Questions