1

I have an array of objects. I want my function clicked() to add a new parameter to my object (visible: false). I'm not sure how to tell react to update my state for a specific key without re-creating the entire array of objects.

First of all, is there an efficient way to do this (i.e using the spread operator)?

And second of all, perhaps my entire structure is off. I just want to click my element, then have it receive a prop indicating that it should no longer be visible. Can someone please suggest an alternative approach, if needed?

import React, { Component } from 'react';
import { DefaultButton, CompoundButton } from 'office-ui-fabric-react/lib/Button';
import { Icon } from 'office-ui-fabric-react/lib/Icon';
import OilSite from './components/oilsite';
import './index.css';

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      mySites: [
        {
          text: "Oil Site 1",
          secondaryText:"Fracking",
          key: 3
        },
        {
          text: "Oil Site 2",
          secondaryText:"Fracking",
          key: 88
        },
        {
          text: "Oil Site 3",
          secondaryText:"Fracking",
          key: 12
        },
        {
          text: "Oil Site 4",
          secondaryText:"Fracking",
          key: 9
        }
      ],
    }
  };

  clicked = (key) => {
    // HOW DO I DO THIS?
  }

  render = () => (
    <div className="wraper">
      <div className="oilsites">
          {this.state.mySites.map((x)=>(
            <OilSite {...x} onClick={()=>this.clicked(x.key)}/>
          ))}
      </div>
    </div>
  )
};

export default App;
1
  • Also for future reference, when defining class methods, you do not want to use arrow functions. Arrow functions lack scope, meaning they have no this. Use a normal function for any class method definition. Commented Jun 3, 2018 at 17:06

3 Answers 3

1

Like this:

clicked = (key) => {
    this.state(prevState => {
      // find index of element
      const indexOfElement = prevState.mySites.findIndex(s => s.key === key);
      if(indexOfElement > -1) {
        // if element exists copy the array...
        const sitesCopy = [...prevState.mySites];
        // ...and update the object
        sitesCopy[indexOfElement].visible = false;
        return { mySites: sitesCopy }
      } 
      // there was no element with a given key so we don't update anything
    })
}
Sign up to request clarification or add additional context in comments.

Comments

0

You can use the index of the array to do a O(1) (No iteration needed) lookup, get the site from the array, add the property to the object, update the array and then set the state with the array. Remeber, map has 3 parameters that can be used (value, index, array).

UPDATE: Fixed Some Typos

class Site
{
  constructor(text, scdText, key, visible=true)
  {
    this.text = text;
    this.secondaryText = scdText;
    this.key = key;
    this.isVisible = visible;
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      mySites: [
        new Site("Oil Site 1", "Fracking", 3),
        new Site("Oil Site 2", "Fracking", 88),
        new Site("Oil Site 3", "Fracking", 12),
        new Site("Oil Site 4", "Fracking", 9)
      ],
    }
    this.clicked = this.clicked.bind(this);
  };
  //Change to a normal function
  clicked(ind)
  {
  //Get the sites from state
    let stateCopy = {...this.state}
    let {mySites} = stateCopy;
    let oilSite = mySites[ind]; //Get site by index
    //Add property to site
    oilSite.isVisible = false;
    mySites[ind] = oilSite;//update array
    //Update the state
    this.setState(stateCopy);

  }

  render = () => (
    <div className="wraper">
      <div className="oilsites">
        {this.state.mySites.map((site, ind) => (
          //Add another parameter to map, index
          <OilSite {...site} onClick={() => this.clicked(ind)} />
        ))}
      </div>
    </div>
  )
};

3 Comments

Thank you. I feel like what I'm trying to accomplish is fairly common. But the code is disproportionately complex. Is my entire approach just really inefficient? How would you approach a task like this?
Not at all, I would've set it up nearly the same way. Where is your data coming from though? Also, if this solved your problem, mark as solution.
I'd probably make a class to hold the data, cleaner and easier to read. Will update my code to reflect
0

I'm not sure how to tell react to update my state for a specific key without re-creating the entire array of objects.

The idea in react is to return a new state object instead of mutating old one.

From react docs on setstate,

prevState is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from prevState and props

You can use map and return a new array.

clicked = (key) => {
    this.setState({
        mySites: this.state.mySites.map(val=>{
             return val.key === key ? {...val, visibility: false} : val
        })
    })
  }

1 Comment

if I use setState((prevState)=>{......}) and address prevState like that, would that be identical to how you used val? Is val the same thing as prevState in this context? Thank you!

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.