Patrick Roberts
Patrick Roberts

Reputation: 51886

Why does this mismatch in lexical scoping occur?

This issue occurs in both Node 6.9.4 and 7.0.0, and I cannot figure out why. I haven't tested in other versions. See comments in Node.js program below:

const express = require('express');
const adaro = require('adaro');

const app = express();

const tabs = require('./config/tabs.json');
const config = require('./config/locals.js');

function getLocals(name) {
  const modifiedTabs = config.tabs.map(tab => {
    return Object.assign(tab, {active: tab.name === name});
  });

  return Object.assign({tab: name}, config, {tabs: modifiedTabs});
}

app.engine('dust', adaro.dust());
app.set('view engine', 'dust');
app.set('x-powered-by', false);

app.use(express.static('static'));

tabs.map(tab => tab.name).forEach(name => {
  const locals = getLocals(name);
  const tab = locals.tabs.find(tab => tab.active);

  // these are always true
  console.log(tab === locals.tabs.find(tab => tab.active));

  function callback(req, res) {
    // const locals = getLocals(name);
    // this should be true, but is false unless the line above is commented in
    console.log(tab === locals.tabs.find(tab => tab.active));
    res.render('main', locals);
  }

  if (tab.url !== '/' + tab.id) {
    app.get(tab.url, callback);
  }

  app.get('/' + tab.id, callback);
});

app.all('*', function (req, res) {
  res.sendStatus(404);
});

app.listen(process.env.PORT || 8000);

Can anyone explain why this is happening and how to fix it?

Upvotes: 0

Views: 25

Answers (1)

Patrick Roberts
Patrick Roberts

Reputation: 51886

I found the issue. In getLocals(), I'm executing

const modifiedTabs = config.tabs.map(tab => {
  return Object.assign(tab, {active: tab.name === name});
});

The Object.assign(tab, ...) overwrites the existing tab object each time it's called, leaving only the last assignments to each tab. So all the views show the last tab in the array as active, since all their active properties have been overwritten with those of the last tab's configuration.

The solution was to switch the arguments to Object.assign() so that the returned object was created rather than overwritten:

return Object.assign({active: tab.name === name}, tab);

Upvotes: 0

Related Questions