-1

I'd come to a situation where I had to get the object value dynamically from an array having object keys coming from an api. I came to this approach by using eval.

class App extends React.Component {

  constructor() {
    super();
    this.state = {
      title: 'Developers',
      descp: 'They are just amazing! JK',
      names: ['title', 'descp']     
    }
  }

  getVal(objKey) {
    let { title, descp } = this.state;
    return eval(objKey);
  }

  render() {
  let {names} = this.state;
    return (
      <div>
        <h2>{this.getVal(names[0])}</h2>
        <div>{this.getVal(names[1])}</div>
      </div>
    )
  }

}

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

The above code works correctly.

Another approach I found later:

render() {
  let {names} = this.state;
    return (
      <div>
        <h2>{this.state[names[0]]}</h2>
        <div>{this.state[names[1]]}</div>
      </div>
    )
  }

Outputs the same result. But my question is that if I use eval with the following case, is it a good approach to do so?

Ahsan Ali
  • 4,951
  • 2
  • 17
  • 27
  • 6
    I'd avoid `eval` unless absolutely necessary. Bracket notation works fine here. –  Jan 22 '18 at 12:02
  • 2
    What did you think `eval` does that made you reach for it? It is neither appropriate nor helpful in this scenario. I'm honestly curious why? – Aluan Haddad Jan 22 '18 at 12:06
  • 2
    Your later approach is correct. I'd avoid the use of `eval()` personally, but never use it when you have a solution that definitely doesn't need it and returns the correct results. – Reinstate Monica Cellio Jan 22 '18 at 12:06
  • is there any reason why you have to keep property names in array instead of explicitly calling them? – pwolaq Jan 22 '18 at 12:08
  • Possible duplicate of [Inject JSX-formatted string into React Component](https://stackoverflow.com/questions/45816830/inject-jsx-formatted-string-into-react-component). Take a look. I think it answers your question perfectly. – Chris Jan 22 '18 at 12:09
  • 3
    @Chris this has nothing to do with React or JSX. – Aluan Haddad Jan 22 '18 at 12:10
  • https://stackoverflow.com/questions/86513/why-is-using-the-javascript-eval-function-a-bad-idea – Pete Jan 22 '18 at 12:10
  • @AluanHaddad, it still answers the question in my opinion. I don't think you even read it considering my answer is at least a 2-minute read and you submitted your comment 20 seconds after I posted the link. – Chris Jan 22 '18 at 12:12
  • Another related link: https://stackoverflow.com/questions/46832912/is-dangerouslysetinnerhtml-the-only-way-to-render-html-from-an-api-in-react/46833353#46833353 – Chris Jan 22 '18 at 12:14
  • 1
    @Chris Like Aluan said, the OP's question has nothing to do with React or JSX. It's about accessing an object's property by String. All the React surrounding it is completely irrelevant. –  Jan 22 '18 at 12:16
  • @Chris while that is true, this is a fundamental question about how JavaScript works. Since JSX is an extended dialect, referencing it to answer such a basic principle is likely to lead to more confusion. – Aluan Haddad Jan 22 '18 at 12:17
  • 1
    @AluanHaddad, I see where you are coming from, being a fundamental JS question and so on. And I agree. However, the links I provided do explain XSS and when it's risky to insert raw html from an api. Perhaps not a 1-to-1 duplicate, but I think they explain the caveats quite well. I'm sure there are better posts out there, but that doesn't render the ones I provided irrelevant. – Chris Jan 22 '18 at 12:32
  • 1
    @Chris Indeed, a good explanation of XSS is important and hard to come by. – Aluan Haddad Jan 22 '18 at 12:34
  • I haven't down voted any of the answer. – Ahsan Ali Jan 22 '18 at 13:06
  • Thank you all for the explanation. I've gone though the answers. – Ahsan Ali Jan 22 '18 at 13:08

2 Answers2

2

Eval should be avoided as it can be very dangerous. You can safely replace your eval call with accessing property via bracket notation.

getVal(objKey) {
    if(this.state.hasOwnProperty(objKey)){
        return this.state[objKey];
    } else {
        // handle missing property
    }
}
pwolaq
  • 6,343
  • 19
  • 45
-1

Eval is generally avoided as it allows the client to insert and evaluate their own expressions in your code.

That being said, JavaScript being a client side language already allows full access to the user, so there isn't really a good reason not to use it.

As long as the user can only mess with their own session, i wouldn't worry. Security should be handled server side anyway so: Beware but don't simply ignore Eval.

EDIT 1 - Defending Eval

The comments pointed out some issues, mainly Performance/Optimization impact, which this answer explains in depth. Basically, since it's Just-In-Time compiling anyway, you don't really lose that much in terms of performance.

As for an example on a use case, here is a template example i whipped up, which also uses the controversial with statement:

var Template = /** @class */ (function () {
    function Template(html) {
        this.html = html;
    }
    Template.prototype.apply = function (params, returnDOMObject) {
        if (params === void 0) { params = {}; }
        if (returnDOMObject === void 0) { returnDOMObject = false; }
        with (params) {
            var html = eval('`' + this.html.replace(Template.regexes.encapsulated, function (n) {
                return n
                    .replace(Template.regexes.start, '${')
                    .replace(Template.regexes.end, '}');
            }) + '`');
        }
        if (returnDOMObject) {
            return document.createRange().createContextualFragment(html);
        }
        return html;
    };
    Template.regexes = {
        encapsulated: new RegExp('{{.*?}}', 'igm'),
        start: new RegExp('\{{2,}', 'igm'),
        end: new RegExp('\}{2,}', 'igm')
    };
    return Template;
}());
//TEST
var persons = [
    { name: "Peter", age: 25 },
    { name: "Ole", age: 55 },
];
var templates = [];
var container = document.body.appendChild(document.createElement("div"));
var leftBox = container.appendChild(document.createElement("div"));
var rightBox = container.appendChild(document.createElement("div"));
leftBox.style.width = rightBox.style.width = "50%";
leftBox.style.height = rightBox.style.height = "500px";
leftBox.style.cssFloat = rightBox.style.cssFloat = "left";
var leftList = leftBox.appendChild(document.createElement("select"));
leftBox.appendChild(document.createElement("br"));
var leftText = leftBox.appendChild(document.createElement("textarea"));
leftText.style.width = "100%";
leftText.style.resize = "vertical";
var rightOutput = rightBox.appendChild(document.createElement("div"));
function updateLists() {
    leftList.innerHTML = '';
    for (var i = 0; i < templates.length; i++) {
        var template = templates[i];
        var option = document.createElement("option");
        option.value = option.innerHTML = template.name;
        leftList.appendChild(option);
    }
}
var h1Template = new Template("<h1>{{name}}</h1>");
var h2Template = new Template("<h2>{{age}} is no age!</h2>");
var pTemplate = new Template("<p>{{name}} may be {{age}}, but is still going strong!</p>\n<p>(When he's {{age*2}} though...)</p>");
var personTemplate = new Template("<p>\n{{ h1Template.apply(params) }}\n{{ h2Template.apply(params) }}\n{{ pTemplate.apply(params) }}\n</p>");
templates.push({ name: "personTemplate", template: personTemplate });
templates.push({ name: "h1Template", template: h1Template });
templates.push({ name: "h2Template", template: h2Template });
templates.push({ name: "pTemplate", template: pTemplate });
function updateOutput() {
    rightOutput.innerHTML = '';
    for (var pi = 0; pi < persons.length; pi++) {
        var person = persons[pi];
        rightOutput.appendChild(personTemplate.apply(person, true));
    }
}
function leftTextChange() {
    templates.find(function (val) { return val.name === leftList.value; }).template.html = leftText.value;
    updateOutput();
}
function leftListChange() {
    leftText.value = templates.find(function (val) { return val.name === leftList.value; }).template.html;
}
updateLists();
leftList.onchange = leftList.onkeyup = leftListChange;
leftText.onchange = leftText.onkeyup = leftTextChange;
leftListChange();
updateOutput();

Here the users input text is being interpreted live, while the user is watching. No security concerns, since it's all client side.

Emil S. Jørgensen
  • 6,216
  • 1
  • 15
  • 28
  • Really, there are lots of engineering reasons, in addition to security reasons, that `eval` should be avoided. This answer might work if you explained why the use of `eval` in the OP is redundant and an example showing an example where it is actually useful. An obvious reason not to use it: It makes the code more complicated and prevents tools from performing even minimal validation. – Aluan Haddad Jan 22 '18 at 12:19