Function to get daily metrics by fetching a record or evaluating a formula
Help you to figure out how to optimize this function is not easy from outside.

Optimization is hard staff and start by collecting numbers.

If you think your function is slow, you should first measure the amount of time that your function run, the size of data that processes, the memory involved and the CPU cycles used.

You could do this with a profiler included in all the modern browsers.

With this numbers you could understand if your function is really bad as you think it is or maybe not.

You said that it is slow, but how about the staff that have to do. In a part you speak about arrays of arrays.

Now if the function is just handling too much its helpful as told us we have to change our approach as the way we though to do is not fit the reality. So no need on wasting time to optimize a function rather find a way to reduce the problem in a manner that is faster to compute.

If the function is very bad, instead, according with our measurements, than it's time to profile inside and start to look where we consume all that time. 

Keep the worst part and try to understand what is the problem and fix this. An then move on to the next less worse.

UPDATE

As was clarified later, the recursion is connected with complex formulas and not with stored values.

A part that is still not clear to me:

const resultMetric = state.metrics[metric_id]
...
const records = getMetricRecords(metric_id, state)


In getMetricRecords() you just look for resultMetric again. As it is an access by key is not an big issue, but for sake of code clarity, if it is the same thing you could consider to move the code of getMetricRecords(). It is not so big staff.

const records = resultMetric.records.map(id =&gt; state.records[id])


the following part could even be simplified:

let metricRecord
if (records.length &gt; 0) {
  for (var i=0; i&lt;records.length; i++) {
    if (moment(records[i].date).isSame(date, 'day')) {
      metricRecord = records[i]
      break
    }
  }
}
if (metricRecord) {
  result = metricRecord
}


Avoiding to check the length of the records as the for loop already do, and remove the variable metricRecord:

for (var i=0; i&lt;records.length; i++) {
    if (moment(records[i].date).isSame(date, 'day')) {
      result = records[i]
      break
    }
}


Of course I don't think this will solve your performance issue, sorry. :(
I just like to remove not-needed code.

About records if is a huge list, and memory is not an issue, you could consider to have an object/map with date as the key so you could avoid the for loop.