Sam Leurs
Sam Leurs

Reputation: 2000

React native no re-render after updating state array

I have the following code (full example):

import React, { useState, useEffect } from 'react';
import { SafeAreaView, View, Button, StyleSheet, Animated } from 'react-native';
import { PanGestureHandler, State } from 'react-native-gesture-handler';

const App = () => {

  const [blocks, setBlocks] = useState([]);

  const CreateBlockHandler = () => {
    let array = blocks;
    array.push({
      x: new Animated.Value(0),
      y: new Animated.Value(0)
    });
    setBlocks(array);
    RenderBlocks();
  };

  const MoveBlockHandler = (index, event) => {
    Animated.spring(blocks[index].x, { toValue: event.nativeEvent.x }).start();
    Animated.spring(blocks[index].y, { toValue: event.nativeEvent.y }).start();
  };

  const RenderBlocks = () => {
      return blocks.map((item, index) => {
        return (
          <PanGestureHandler key={index} onGestureEvent={event => MoveBlockHandler(index,event)}>
            <Animated.View style={[styles.block, {
              transform: [
                { translateX: item.x },
                { translateY: item.y }
              ]
            }]} />
          </PanGestureHandler>
        )
      });
  };


  return (

    <SafeAreaView style={styles.container}>
      <View style={styles.pancontainer}>
        <RenderBlocks />
      </View>
      <Button title="Add block" onPress={CreateBlockHandler} />
    </SafeAreaView>

  );

};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  pancontainer: {
    width: '95%',
    height:'75%',
    borderWidth: 1,
    borderColor: 'black'
  },
  block: {
    width: 50,
    height: 50,
    backgroundColor: 'black'
  }
});

export default App;

What does this code do? It's a big square, and a button below it. When I click on the button, a new black square (50x50) is made in the big square. I do this by creating a new array element (the array = blocks). This is done in the function CreateBlockHandler. This does not work correctly!

The function MoveBlockHandler makes the little squares movable. This works!

What does not work? When I create a new black square, the black square is not rendered on the screen. Only when I refresh, the square is rendered. The square is created through CreateBlockHandler, because when I do a console.log(blocks) in that function, I can see that a new array element is added.

How can I force this code to do a full re-render with all the array elements? I tried to wrap the render of the square in a separate function (RenderBlocks) and I'm calling this function every time a new square is made (last line in CreateBlockHandler). The function is called (I can check this with a console.log()) but no squares are rendered.

Upvotes: 1

Views: 2674

Answers (2)

kooskoos
kooskoos

Reputation: 4859

When you assign blocks to array the reference gete copied which mutates the state, so it doesn't re-render on setState.

 const CreateBlockHandler = () => {
    let array = [...blocks];
    array.push({
      x: new Animated.Value(0),
      y: new Animated.Value(0)
    });
    setBlocks(array);
    RenderBlocks

Upvotes: 6

Sami Hult
Sami Hult

Reputation: 3082

There are multiple issues with your code.

As kooskoos pointed out, your state remains referentially equal (it's the same array, only the elements change). This will not trigger re-render.

Also, you are manipulating state of the App component. RenderBlocks component's props and state remain unchanged which implies that they don't need to be re-rendered. Since the component is an anonymous function and is recreated during every render of App, it probably gets re-rendered anyways.

In addition, you are directly calling RenderBlocks, which looks like a component. That is unnecessary and will do nothing here, but if it had any hooks, it would cause problems.

You should probably also conform to the convention that components are PascalCase capitalised and callbacks snakeCase capitalised.

Upvotes: 0

Related Questions