1

I am doing a fetch request to receive all my json data and store it in my current state, afterwards I want to call a function that will change the content of one of the nested json objects inside of an array.

I've looked at similar questions like this one but I can't seem to figure it out.

Here is the structure of my received json data:

[
   {
      "id":"George",
      "top":[
         {
            "performance":4289000,
            "day":"Sunday",
            "penalties":"none"
         },
         {
            "performance":4259000,
            "day":"Saturday",
            "penalties":"two"
         }
      ]
   },
   {
      "id":"Maya",
      "top":[
         {
            "performance":4139000,
            "day":"Monday",
            "penalties":"none"
         },
         {
            "performance":4439000,
            "day":"Sunday",
            "penalties":"one"
         }
      ]
   }
]

how can I perform a function on the performance object of the array top and pass it to the full state? For an example, how can I call a function for transforming milliseconds to a duration on this nested object?

Here is my function for changing time duration:

function parseMillisecondsIntoReadableTime(milliseconds) {
    //Get hours from milliseconds
    var hours = milliseconds / (1000 * 60 * 60);
    var absoluteHours = Math.floor(hours);
    var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours;
    //Get remainder from hours and convert to minutes
    var minutes = (hours - absoluteHours) * 60;
    var absoluteMinutes = Math.floor(minutes);
    var m = absoluteMinutes > 9 ? absoluteMinutes : '0' + absoluteMinutes;
    //Get remainder from minutes and convert to seconds
    var seconds = (minutes - absoluteMinutes) * 60;
    var absoluteSeconds = Math.floor(seconds);
    var s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds;
    return h + ':' + m + ':' + s;
}

Question:

How can I apply the function parseMillisecondsIntoReadableTime to the performance object inside the top array and set it to this.state fore I render it to the html page?

1
  • if the parsed performance value is only for presentational purposes you could call the parseMillisecondsIntoReadableTime in the render. Like a filter. Commented Mar 20, 2019 at 8:59

3 Answers 3

1

It's a rule we shouldn't mutate the state.

Therefore, in the cause you have to update the nested state performance key:

  1. First you have to loop over the current state data, in order to create a new copy of the array.
  2. Secondly, you can overwrite the performance key, applying parseMillisecondsIntoReadableTime function.
  3. Finally, having the new state, you can update your component state, using the data from newState constant (from the example below).

Example (how to construct the new array, without mutations):

// Credits to @Tholle, for refactoring the below function.
const parseMillisecondsIntoReadableTime = duration => {
  let seconds = Math.floor((duration / 1000) % 60);
  let minutes = Math.floor((duration / (1000 * 60)) % 60);
  let hours = Math.floor((duration / (1000 * 60 * 60)) % 24);

  hours = hours < 10 ? "0" + hours : hours;
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;

  return hours + ":" + minutes + ":" + seconds;
}

const state = [{
    "id": "George",
    "top": [{
      "performance": 4289000,
      "day": "Sunday",
      "penalties": "none"
    }, {
      "performance": 4259000,
      "day": "Saturday",
      "penalties": "two"
    }]
  },
  {
    "id": "Maya",
    "top": [{
      "performance": 4139000,
      "day": "Monday",
      "penalties": "none"
    }, {
      "performance": 4439000,
      "day": "Sunday",
      "penalties": "one"
    }]
  }
]

const newState = state.map(item => ({
  ...item,
  top: item.top.map(top => ({
    ...top,
    performance: parseMillisecondsIntoReadableTime(top.performance)
  }))
}))

console.log(newState)

If you have questions, feel free to ask.

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

4 Comments

Hey Jodan, thanks for the assistance so far. I'm having issues fitting this inside my code since I am using a fetch request, can you help me out with that by any chance? Here is the fiddle
Hey, I've applied the way I've constructed the newState in my answer. Here's the fiddle: jsfiddle.net/jordan_enev/4z1k3786/4
Thanks a lot for the help. I am getting error! result.map is not a function but I will figure this out on my own. Can I ask you a favor? Can you suggest any good reads on react that go beyond the basic tutorials? Thank you a lot for the assistance, I mean it.
Hm .. The fiddle I've provided you works and doesn't return an error. If the answer helped you, please consider voting / accepting it. About React resources - when I started with React, I began with understanding the React Main Concepts and Principles (from the Documentation) and after that I completed a React Udemy course.
1

You can process the data before you put it in state by looping over all the elements in the array and all the elements in the nested top arrays and overwrite performance with the result of a call to parseMillisecondsIntoReadableTime.

Example

const data = [
  {
    id: "George",
    top: [
      {
        performance: 4289000,
        day: "Sunday",
        penalties: "none"
      },
      {
        performance: 4259000,
        day: "Saturday",
        penalties: "two"
      }
    ]
  },
  {
    id: "Maya",
    top: [
      {
        performance: 4139000,
        day: "Monday",
        penalties: "none"
      },
      {
        performance: 4439000,
        day: "Sunday",
        penalties: "one"
      }
    ]
  }
];

function getData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(data);
    }, 1000);
  });
}

function parseMillisecondsIntoReadableTime(duration) {
  let seconds = Math.floor((duration / 1000) % 60);
  let minutes = Math.floor((duration / (1000 * 60)) % 60);
  let hours = Math.floor((duration / (1000 * 60 * 60)) % 24);

  hours = hours < 10 ? "0" + hours : hours;
  minutes = minutes < 10 ? "0" + minutes : minutes;
  seconds = seconds < 10 ? "0" + seconds : seconds;

  return hours + ":" + minutes + ":" + seconds;
}

class App extends React.Component {
  state = {
    data: null
  };

  componentDidMount() {
    getData().then(data => {
      data.forEach(item => {
        item.top.forEach(obj => {
          obj.performance = parseMillisecondsIntoReadableTime(obj.performance);
        });
      });

      this.setState({ data });
    });
  }

  render() {
    const { data } = this.state;

    if (data === null) {
      return null;
    }
    return <div>{JSON.stringify(data)}</div>;
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

2 Comments

yes this works with static data place inside the react file but I am trying to do it with a fetch request. Also when I tried to implement the code, I received an error on getData() not defined. Would an already created fiddle make this easier? Here's a link
@someonewithakeyboard getData is just an example since I don't have access to you API. I used it to demonstrate what to do when you get the data asynchronously. Try to map the componentDidMount in my answer to your own API logic.
0

if the parsed performance value is only for presentational purposes you could call the parseMillisecondsIntoReadableTime in render - Like a filter.

// simplified single data object.
const data = {
  "id": "George",
  "top": [{
    "performance": 4289000,
    "day": "Sunday",
    "penalties": "none"
  }, {
    "performance": 4259000,
    "day": "Saturday",
    "penalties": "two"
  }]
}

const parseMillisecondsIntoReadableTime = milliseconds => {
  //Get hours from milliseconds
  let hours = milliseconds / (1000*60*60);
  let absoluteHours = Math.floor(hours);
  let h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours;
  //Get remainder from hours and convert to minutes
  let minutes = (hours - absoluteHours) * 60;
  let absoluteMinutes = Math.floor(minutes);
  let m = absoluteMinutes > 9 ? absoluteMinutes : '0' +  absoluteMinutes;
  //Get remainder from minutes and convert to seconds
  let seconds = (minutes - absoluteMinutes) * 60;
  let absoluteSeconds = Math.floor(seconds);
  let s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds;
  return h + ':' + m + ':' + s;
}

// example render function
render () {
  return (
    data.top.map(t => (
      <p>{parseMillisecondsIntoReadableTime(t.performance)}</p>
    ))
  )
}

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.