4

I have an array of objects inside my class that I am modifying and only when a keypress happens do I want to render this object visually.

class Example extends React.Component {
    constructor(props) { 
       super(props); 
       this.myArr = []; // this is an array of objects 
    }

    render() {  
       return (
        ???
       );
    }
}

Now I modify the contents of this.myArr in many different methods. And only when I'm ready (on a keypress or some other event) do I want to render it.

Now in my render() should I have a reference to this.myArr and then use this.forceUpdate() when I want to force a re-render.

Or should I move myArr into this.state.myArr, and modify this.state.myArr in my methods and when I am ready to display it, in my render() reference to this.state.myArr, and somehow force a rerender with this.setState(myArr: this.state.myArr);

Shai UI
  • 50,568
  • 73
  • 204
  • 309

6 Answers6

3

***Second Update - I think this may be what you want. Obviously, you'll need to add a lot of logic for your mouse click events. It should point you in the right direction though.

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.myArr = [];

    this.state = {
      myArr: [{ width: 10 }, { width: 20 }], // header widths
    };
  }

  // call changeHeaders when needed
  // it will update state, which will cause a re-render
  changeHeaders = (column, newWidth) => {
    const newArr = [...this.state.myArr];

    if (newArr[column]) {
      newArr[column].width = newWidth;
    }

    this.setState({ myArr: newArr });
  }

  renderArray = () => {
    return this.state.myArr.map(({ width }) => <div>{width}</div>);
  }

  render() {
    return (
      <div>
        {this.renderArray()}
      </div>
    );
  }
}
twils0
  • 2,431
  • 2
  • 12
  • 24
  • keyPressed could be some kind of event... (not necessarily a keypress). But you're saying I should name this event as a bool ... interesting. – Shai UI May 24 '18 at 23:45
  • Sure - I didn't really get into actually capturing key press events. It could be anything you want. I assumed you were alright on that front. The main thing is: you want the keyPressed bool in state, so that you can toggle it on and off, to simultaneously re-render your component and to show this.myObj. Then, when you want to hide it, you update keyPressed to false, the component will re-render, and this.myObj will be hidden again. Overall, you should avoid using forceUpdate() at all costs. It's rarely necessary and not best-practice in React, unless it's unavoidable. – twils0 May 24 '18 at 23:49
  • hmmm.. actually I don't think this will work. what I'm doing is changing the width of headers in a grid, when a user changes the widths with his mouse. and the width of headers is inside this.myArr[columnIndex].width ... so it needs to rerender when one of these widths changes. – Shai UI May 24 '18 at 23:53
  • You're changing this up on me here haha. I will update in a second. Hold on. – twils0 May 25 '18 at 00:04
  • Thank you for your help. seems like a bunch of bs though, easier to just modify my class object and call forceUpdate when I'm done and have the render use my class object. – Shai UI May 25 '18 at 19:12
  • 1
    @foreyez You're right about performance. Calls to setState are actually async (though you often may not notice) and are batched for performance gains. React is also smart about what it actually updates, particularly if you use a [PureComponent] (https://reactjs.org/docs/react-api.html#reactpurecomponent). If you're looking for a mutable state management library, I would recommend [mobx] (https://github.com/mobxjs/mobx). It will mutate state and re-render for you. – twils0 May 26 '18 at 00:32
0

Either way would work but I think its better practice to use this.state to hold your array and use this.setState() to force a re-render and call the this.setState within your keypress event callback

Here is how you update your array value - Correct modification of state arrays in ReactJS

There's a very good explanation why you should avoid using this.forceUpdate() in here answered by Chris.

Robert Chan
  • 144
  • 11
  • if I put it in this.state as you're saying, can I modify this.state.myArr in my methods, and rerender it with this.setState(myArr: this.state.myArr); – Shai UI May 24 '18 at 23:50
  • Yes, you can overwrite your array value and use setState to update the view – Robert Chan May 24 '18 at 23:52
  • No, you should never modify state directly. Create a copy of an array, mutate it in any way you like, then update the state with the new array. – Kit Isaev May 24 '18 at 23:59
  • @KitIsaev there's what i mean... using setState to set the array value. I did not mean to modify the this.state.arrayValue directly – Robert Chan May 25 '18 at 00:01
  • @RobertChan I see, but I think he meant **mutating** this.state.myArr, which is a bad idea – Kit Isaev May 25 '18 at 00:03
  • 1
    @KitIsaev Correct. As the React doc states you should never mutate the state values directly. In my answer, I have listed a link about the correct way to update array value via setState and also a link about why you should avoid using this.forceUpdate when possible – Robert Chan May 25 '18 at 00:05
  • @RobertChan I guess he didn't read and intended to mutate the state. I think so because of the fact that he was going to update state with the current state `this.setState({myArr: this.state.myArr})`, which makes me suppose that he intends to use setState as force rerender after updating the state by a direct mutation. – Kit Isaev May 25 '18 at 00:10
0

In this case you should use state. State is intended to be used for any data that affect how your component looks. Remember that you may not modify your state directly, it's an antipattern. Instead, you should create a copy of the array and update the state with that modified copy. Like so:

class Example extends React.Component {
  constructor(props) { 
    super(props); 
    this.state = {myArr: []}; // this is an array of objects 
  }

  mutateArraySomehow() {
    const nextArr = [...this.state.myArr];
    nextArr.push('heyyoooo');
    this.setState({myArr: nextArr});
  }

  render() {  
    return (
      ???
    );
  }
}
Kit Isaev
  • 680
  • 8
  • 19
0

This is how i would have done it

class Example extends React.Component {
   constructor(props) { 
      super(props);
      this.state = {
       myArr: [],
       display: false
      }
   }

   render() {
      if(this.state.display) {
       return (
          <MyComponent onKeyPress=(ev=>{
              this.setState({display: true})
          })
         />
       );
      } else {
       return (<div></div>)
      }
   }
}
andylamax
  • 1,858
  • 1
  • 14
  • 31
0

When there is a modification to the array elements, you need to do a setState of status to true.This would perform the conditional rendering and will display the modified array.

class Example extends React.Component {
  constructor(props) { 
    super(props); 
    this.state = {
      myArr = []; // this is an array of objects 
      status:false
    }
  }

  modifyArr = () => {
    //some array modifications
    this.setState({status:true})
  }

  render() {  
    return (
      {this.state.status ? null : (<div>`Display your array here`</div>)}
    );
  }
}
rrd
  • 5,789
  • 3
  • 28
  • 36
Erick
  • 1,098
  • 10
  • 21
0

In short, define state inside class like:

state: {
   firstName: String
} = {
   firstName: ''
}

And inside render function you would do this:

this.setState({ firstName: 'Junior' })
Gabriel Arghire
  • 1,992
  • 1
  • 21
  • 34