1

My goal of this code:

  1. Render some view elements with a loop
  2. Inside the loop, set the state
  3. On clicking the elements, update that value

Here is my code:

import React, {Component} from 'react';
import {View, Text, TouchableOpacity} from 'react-native';

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      maths: {},
    };
  }

  prepareMaths = function() {
    var count = 5;

    var updateMath = key => {
      var stateMaths = this.state.maths;
      stateMaths['position_' + key] = Math.random();

      this.setState({maths: stateMaths}, () => {
        console.log(this.state.maths);
      });
    };

    var stateMaths = this.state.maths;

    return [...Array(count)].map((options, key) => {
      stateMaths['position_' + key] = Math.random();
      this.setState({maths: stateMaths});

      return (
        <TouchableOpacity
          key={key}
          onPress={() => updateMath(key)}
          style={{
            height: 100,
            width: 200,
            marginRight: 20,
            marginBottom: 10,
            backgroundColor: 'green',
          }}>
          <Text>{this.state.maths['position_' + key]}</Text>
        </TouchableOpacity>
      );
    });
  };

  render() {
    return (
      <View>
        <View>{this.prepareMaths()}</View>
      </View>
    );
  }
}

I'm getting this error with this code:

enter image description here

I'm very confused. Because if I remove setState... code inside the loop, it's showing random maths naturally. But how? Since I'm using this.state.maths['position_' + key] on render. I really don't know how that data is generating.

Please help. Thanks in advance.

6
  • 2
    You can't update state from the render function as that will trigger another render cycle. Commented Nov 4, 2020 at 8:22
  • @DrewReese, Thanks. But how can I do it? Please provide a solution. Commented Nov 4, 2020 at 9:22
  • What is the purpose of this.setState({maths: stateMaths}); in the mapping loop? Commented Nov 4, 2020 at 9:28
  • This will save data to state for the first time, because that data is empty in constructor. Commented Nov 4, 2020 at 9:53
  • 1
    Yup, gave it all a deeper look and suspected it was an attempt to provide the "initial state" to render something. Commented Nov 4, 2020 at 10:01

1 Answer 1

1

Issues

  1. State mutations

    var stateMaths = this.state.maths; // <-- state
    stateMaths['position_' + key] = Math.random(); // <-- mutation!!
    
  2. Updating state in render function causes rerender. render is a pure function with zero side-effects

    return [...Array(count)].map((options, key) => {
      stateMaths['position_' + key] = Math.random();
      this.setState({maths: stateMaths}); // <-- trigger rerender
    

Solution

  1. Factor prepareMaths and updateMath into standalone utility functions
  2. Convert maths state to array
  3. Use componentDidMount to initialize state
  4. Use componentDidUpdate to log updated state
  5. Move the JSX from prepareMaths to render function for mapping from state

Updated component

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      maths: [],
    };
  }

  componentDidMount() {
    this.prepareMaths();
  }

  componentDidUpdate() {
    console.log(this.state.maths);
  }

  updateMath = (key) => {
    this.setState((prevState) => ({
      maths: prevState.maths.map((el, i) => (i === key ? Math.random() : el)),
    }));
  };

  prepareMaths = function () {
    const count = 5;

    this.setState({ maths: [...Array(count)].map(Math.random) });
  };

  render() {
    const { maths } = this.state;
    return (
      <View>
        <View>
          {maths.map((value, key) => (
            <TouchableOpacity
              key={key}
              onPress={() => this.updateMath(key)}
              style={{
                height: 100,
                width: 200,
                marginRight: 20,
                marginBottom: 10,
                backgroundColor: 'green',
              }}>
              <Text>{value}</Text>
            </TouchableOpacity>
          ))}
        </View>
      </View>
    );
  }
}

Expo Snack Demo

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.