简体   繁体   English

嵌套对象值的 JSX 条件渲染

[英]JSX Conditional Rendering for Nested Object Values

I am attempting to have a "flashcard" show no background color when it is unanswered, green when it is correctly answered, and yellow when it is incorrectly answered.我试图让“抽认卡”在未回答时不显示背景颜色,正确回答时显示绿色,错误回答时显示黄色。 The properties for each card are stored in a nested object.每张卡片的属性都存储在一个嵌套对象中。 I am having issues using conditional rendering to properly show my content.我在使用条件渲染来正确显示我的内容时遇到问题。

Code: Here is what I want to accomplish, but the JSX conditional statement is only registering the last statement in the className.代码:这里是我想要完成的,但是JSX条件语句只是在className中注册了最后一个语句。

<div className="row">
    {Object.keys(this.state.selections).map( (lang, index) => (
        <>
            {Object.keys(this.state.selections[lang]).map( (cat, index) => (
                <>
                {Object.keys(this.state.selections[lang][cat]).map( (sect, index)=> (
                    <>
                    {Object.keys(this.state.selections[lang][cat][sect]).map( (letter, index)=> (
                        <>
                        {this.state.selections[lang][cat][sect][letter].show &&
                            <div className={
                                (this.state.selections[lang][cat][sect][letter].correct ? correct: unanswered),
                                (this.state.selections[lang][cat][sect][letter].incorrect ? incorrect : unanswered)
                            } key= {index}>
                            </div>
                        }
                        </>
                    ))}
                    </>
                ))}
                </>
            ))}
        </>
    ))}
</div>

Essentially, I want to place a conditional statement in the className to change the background based off the object property values being true/false.本质上,我想在 className 中放置一个条件语句,以根据对象属性值为 true/false 来更改背景。

Obj model:对象模型:

{
    "Hiragana": {
        "Main Kana": {
            "Vowels": {
                "a": {
                    "characterName":  "A",
                    "character":  "あ",
                    "unicode":  "\u0026‌#12354;",
                    "hexEncoding":  "\u0026‌#x3042",
                    "englishTranslation":  "a",
                    "alternateEnglishTranslation":  "",
                    "isLetter":  "1",
                    "alphabet":  "hir",
                    "show": false,
                    "answer": "",
                    "correct": false,
                    "incorrect": false,
                    "unanswered": true
                    },

Bonus question: Having to loop through each object layer seems a bit messy and tedious... Would there be any good way to shorten this up?额外问题:必须遍历每个对象层似乎有点混乱和乏味......有什么好方法可以缩短它吗? The lang and cat are selected by input so they cannot be hard-coded. langcat是通过输入选择的,因此它们不能被硬编码。 While my current solution "works" I want to improve myself when possible.虽然我目前的解决方案“有效”,但我想尽可能提高自己。

Update: Working code from the solution offered by @CertainPerformance更新: @CertainPerformance 提供的解决方案中的工作代码

<div className="row">
    {Object.keys(this.state.selections).map(lang => 
        Object.values(this.state.selections).map( (cat) =>
            Object.values(cat).map((sect, indexCat) =>
                Object.values(sect).map((sectLetters, indexSect) =>
                    Object.values(sectLetters).map((letter, indexSectL) =>
                        letter.show &&
                        <div
                            className={getBgClass(letter)}
                            key={indexSectL}>
                            <div>
                                <h5 className="card-title" >{letter.character}</h5>
                                <input data-lang={lang} data-cat={Object.getOwnPropertyNames(cat)[indexCat]} data-sect={Object.getOwnPropertyNames(sect)[indexSect]} type="text" name={letter.characterName} value={letter.answer} onChange={this.handleChange.bind(this)} />
                            </div>
                        </div>
                    )
                )
            )
        )
    )}
</div>

For those confused by my explanation of accessing the array generated by Object.values, here is an example with simple console.log to better explain.对于那些对我访问 Object.values 生成的数组的解释感到困惑的人,这里有一个带有简单 console.log 的示例以更好地解释。

Object.values(this.state.selections).map((cat) =>{
    Object.values(cat).map((sect, indexCat) => {
        console.log("Cat: ", Object.getOwnPropertyNames(cat)[indexCat]);
    });
});

I'm not sure if your current code works as you're expecting it to.我不确定您当前的代码是否像您期望的那样工作。 Here:这里:

className={
  (this.state.selections[lang][cat][sect][letter].correct ? correct : unanswered),
  (this.state.selections[lang][cat][sect][letter].incorrect ? incorrect : unanswered)
}

you're invoking the comma operator, which evaluates to only the final expression in the comma-separated list.您正在调用逗号运算符,它仅计算逗号分隔列表中的最终表达式。 It's equivalent to:它相当于:

className={
  (this.state.selections[lang][cat][sect][letter].incorrect ? incorrect : unanswered)
}

To fix it, either use a nested conditional operator, or pass it into a function.要修复它,请使用嵌套条件运算符,或将其传递给函数。

To make your code more concise, instead of iterating over the keys of the object, iterate over the values of the object.为了使您的代码更简洁,而不是迭代对象的,而是迭代对象的 You also don't need a fragment <></> when returning an array.返回数组时也不需要片段<></>

<div className="row">
    {Object.values(this.state.selections).map(selection =>
        Object.values(selection).map(cat =>
            Object.values(cat).map(sect =>
                Object.values(sect).map((letter, index) =>
                    letter.show &&
                    <div
                        className={
                            letter.correct
                                ? correct
                                : letter.incorrect
                                    ? incorrect
                                    :
                                    unanswered
                        }
                        key={index}
                    >
                    </div>
                )
            )
        )
    )}
</div>

To avoid the nested conditional, if that's your preference, do something like:为了避免嵌套条件,如果这是您的偏好,请执行以下操作:

<div
    className={getClass(letter)}
    key={index}
></div>
const getClass = (letter) => {
  if (letter.correct) return correct;
  if (letter.incorrect) return incorrect;
  return unanswered;
};

You could also consider changing your model so that the question state (correct/incorrect/unanswered) is a single property, rather than multiple, eg, instead of您还可以考虑更改您的模型,以便问题状态(正确/不正确/未回答)是单个属性,而不是多个属性,例如,而不是

"correct": false,
"incorrect": false,
"unanswered": true

have

answerState: 2

where 2 corresponds to unanswered, 1 corresponds to incorrect, and 0 corresponds to correct - or something like that.其中 2 对应于未回答,1 对应于不正确,0 对应于正确 - 或类似的东西。 Then you can use an array to look up the appropriate class name:然后你可以使用一个数组来查找合适的类名:

className={classNames[letter.answerState]}
const classNames = [correct, incorrect, unanswered];

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM