0

I'm trying to set state while adding a key value to the array. I've followed several questions including this one: How to add a new key value to react js state array?

I can't get any of those answers to work with my challenge.

My state is like this:

this.state = { jsonReturnedValue: [] }

when componentDidMount() I make a fetch request and I added new state and a foreach loop (as based on the instructions from the question above)

  componentDidMount() {

    let newState = [...this.state.jsonReturnedValue];
    newState.forEach(function(file) {
      file.selected = "false"
    })

    fetch('http://127.0.0.1:8000/api/printing/postcards-printing')
      .then(response => response.json())
      .then(json => {
      this.setState({ jsonReturnedValue: [...this.state.jsonReturnedValue, ...json.printCategory.products] }, () => console.log(this.state));
      })
      .then(this.setState({jsonReturnedValue: newState}, () => console.log("selected added" + this.state) ));
  }

I was thinking that I would just setState again with the key value pair so I added the .then the third time but it's not working.

the final console log returns this: selected added[object Object] Digging more into it I see the object is empty.

EDIT: My goal is to set the state with the api call but also add key value of "selected: false" to each array.

FabricioG
  • 3,107
  • 6
  • 35
  • 74
  • 1
    the console log will look like that no matter what because you're trying to concatenate a string and an object. so if you change the log to `console.log('selected added', this.state)` does it still come back incorrect? – Joey Oct 24 '18 at 22:15
  • Not much of this makes sense. You just want to set the state when the fetch call completes? – Jared Smith Oct 24 '18 at 22:19
  • My goal is to add the api call to state but also add the key / value of "selected: false" to each array @Jared Smith – FabricioG Oct 24 '18 at 22:21
  • `.then(this.setState` your using `then` incorrectly, then expects a function callback. eg.. `.then(() => this.setState(....` – Keith Oct 24 '18 at 22:26
  • 1
    you should describe in your question what kind of data structure gets returned from the api and what should state look like after it's updated – Oluwafemi Sule Oct 24 '18 at 22:29

4 Answers4

2

componentDidMount only runs once, the first time the component is mounted, which is before your data call. After you fetch your data and call setState, the update lifecycle methods are run.

Assuming you are on a new version of React, you'll want to intercept your state change in getDerivedStateFromProps:

getDerivedStateFromProps(props, state) {
  // `state` will be the new state that you just set in `setState`
  // The value returned below will be the new state that the `render`
  // method sees, and creating the new state here will not cause a re-render
  return state.jsonReturnedValue.map((file) => {
    file.selected = "false";
    return file;
  });
}

Now, Ideally you don't actually want to do that there, but instead do it once you fetch your data:

fetch('http://127.0.0.1:8000/api/printing/postcards-printing')
      .then(response => response.json())
      .then(json => {
        // NOTE: it's unclear from your post exactly what the data structure is
        // Is it product.file, or is `product` also `file`?
        const products = json.printCategory.products.map((product) => {
          product.file = "selected";
          return product;
        });

        this.setState(
          {
            jsonReturnedValue: [...this.state.jsonReturnedValue, ...products],
          },
          () => console.log(this.state)
        );
      });
Matthew Herbst
  • 29,477
  • 23
  • 85
  • 128
0

should probably be something like this

function addSelectedFalse(array) {
    return array.map(item => ({
        ...item,
        selected: false
    })

fetch('endpoint')
    .then(response => response.json())
    .then(json => this.setState({ jsonReturnedValue: [...addSelectedFalse(this.state.jsonReturnedValue), ...addSelectedFalse(json.printCategory.products] }))
Joey
  • 174
  • 2
  • 4
0

From my understanding you want to fetch a products array and add selected: false to each product before setting state:

constructor(props) {
    super(props);
    this.state = {
        products: []
    }
}
componentDidMount() {
    fetch('http://127.0.0.1:8000/api/printing/postcards-printing')
    .then(response => response.json())
    .then(json => {
        const { products } = json.printCategory;
        for (let i = 0; i < products.length; i++) {
            products[i].selected = false;
        }
        this.setState({products});
    });
}
wdm
  • 7,121
  • 1
  • 27
  • 29
0

Here an idea working:

https://codepen.io/ene_salinas/pen/GYwBaj?editors=0010

  let { Table } = ReactBootstrap;

  class Example extends React.Component {
    constructor(props, context) {
      super(props, context);

      this.state = {
          products: []
      }
    }

    componentDidMount() {
      console.log('componentDidMount..')
      fetch('https://api.github.com/users/xiaotian/repos')
        .then(response => response.json())
        .then(output => {
          let products = []
          for (let i = 0; i < output.length; i++) {
              products.push({selected:false,name:output[i].name})
          }

          this.setState({products},() => console.log(this.state))

      })

    }

    render() {

          return(<Table striped bordered condensed hover>
            <thead>
              <tr>
                <th>Selected</th>
                <th>Name</th>
              </tr>
            </thead>
            <tbody>
              {this.state.products.map((item, i) => {
                return (
                    <tr><td><input type="checkbox" checked={item.selected}/></td><td>{item.name}</td></tr>
                ) 
              })}
            </tbody>
          </Table>)
    }
  }

  ReactDOM.render(
    <Example />, 
    document.getElementById('app')
  );

Hope to help you!

ene_salinas
  • 705
  • 4
  • 11