javascriptreact-nativereact-reduxreact-animations

How to animate reordering of reducer array state in React Native LayoutAnimation


How can I achieve a reordering animation using React Native LayoutAnimation when a Redux reducer's property is updated?

I have a players array in a Redux reducer, looks something like this:

enter image description here

playersReducer.players:

[{
    "uuid": "681b04b5-5b39-4c6c-b21d-8a8a2240a0c7",
    "name": "Tom",
    "score": 190,
    "online": false,
    "isMe": null
}, {
    "uuid": "02531db4-e61d-4754-bb4a-fef1a696bef4",
    "name": "Dick",
    "score": 10,
    "online": false,
    "isMe": null
}, {
    "uuid": "51420a54-7563-4cd2-adf3-ccefc1b6ffca",
    "name": "Harry",
    "score": 170,
    "online": false,
    "isMe": null
}];

When a reducer gets an update via a websocket response the score property changes on the appropriate array object. I can confirm with the Chrome redux browser plugin that each object state is remaining immutable and the diff shown proves that;

enter image description here

When the reducer updates my functional component's state I reorder the playersReducer.players array with the sort function to order the players by score as so:

let players = [...props.playersReducer.players];

return (
    {players
        .sort((player1, player2) => (player1.score > player2.score) ? -1 : 1 )
        .map((player, i) => {
           return (
              // player UI rendered here   
           )

I've setup LayoutAnimation.configureNext() in the body of the function component, which does successfully render a new item being added the array from another reducer which proves LayoutAnimation is working;

    const isFirstRender = React.useRef(true);
    React.useEffect(() => {
        if (isFirstRender.current) {
            isFirstRender.current = false;
            return;
        }
        /*business logic for component did update*/
        LayoutAnimation.configureNext({
            ...LayoutAnimation.Presets.easeInEaseOut,
            create: {property: "scaleXY"},
            delete: {property: "scaleXY"},
            update: {property: "scaleXY"}
        })
        
    });

How can I use LayoutAnimation to animate a reordering effect on my player components? I'm trying to achieve something like this:

enter image description here


Solution

  • You can achieve this goal using Transitioning View.

    import React, { useRef, useState } from 'react';
    import { Button, SafeAreaView, FlatList, StyleSheet, Text, View } from 'react-native';
    import { Transition, Transitioning } from 'react-native-reanimated';
    
    const transition = (
      <Transition.Together>
        <Transition.Change durationMs={500} />
      </Transition.Together>
    );
    
    export default App = () => {
      const [data, setData] = useState([2, 4, 3, 1]);
      const ref = useRef();
    
      return (
        <SafeAreaView style={{ flex: 1 }}>
          <Transitioning.View ref={ref} transition={transition}>
            <FlatList
              data={data}
              keyExtractor={(item) => item.toString()}
              renderItem={({ item }) => {
                return (
                  <View style={styles.item}>
                    <Text style={styles.itemText}>{item}</Text>
                  </View>
                );
              }}
            />
          </Transitioning.View>
          <Button
            title="Reorder"
            onPress={() => {
              ref.current.animateNextTransition();
              const sortedData = [...data];
              sortedData.sort();
              setData(sortedData);
            }}
          />
        </SafeAreaView>
      );
    };
    
    const styles = StyleSheet.create({
      item: {
        width: '100%',
        height: 100,
        backgroundColor: 'red',
        marginVertical: 10,
        justifyContent: 'center',
        alignItems: 'center',
      },
      itemText: {
        fontSize: 21,
        color: '#fff',
      },
    });
    

    You can check the demo here.