浅析工作中遇到的组件循环渲染问题
需求背景
通过一个字符串数组中动态渲染 Dropdown
组件,句子 be like: ["一个", "keyword", "的", "keyword", "keyword"]
,渲染是需要将 keyword
替换为二次封装后的 DropdownBox
组件。看似很简单的一个需求,但遇到的问题就是 keyword
是有严格顺序的渲染,也就是说一个静态文件中读取 keyword
列表,按索引顺序(从 0 开始)排列渲染。
根据需求准确来说,在循环中,第一次页面渲染出 DropdownBox
时,传入这个组件的 index
必须为 0
,第二次渲染出 DropdownBox
时传入的 index
为 1
。
渲染方法的具体代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const Test = () => {
const [arr, setArr] = useState(["一个", "keyword", "的", "keyword", "keyword"])
return(
<div>
{arr.map((item, index) => (
<div className="keywords" key={index}>
{item !== "keyword"
?
<> {item} </>
:
<>
<DropdownBox keyword={getCurrentKeyword(index)}
menu={keywordList[index]}
getUserKeywordList={getUserKeywordList}
keywordIndex={keywordIndex(index)}
/>
{index !== moduleItem?.keywords.length - 1 && <> </>}
</>
}
</div>
))}
</div>
)
}
怎么将组件传入的属性 keywordIndex
和 DropdownBox
组件本身进行强关联呢?
技术实现
1
2
3
4
5
6
7
8
// 在外部定义一个对象来存储关键字索引
// 在依赖项发生变化时自动进行数据重置
const keywordIndexes = useMemo(() => ({}), [moduleItem])
// 检查关键字是否已经存在于索引对象中,如果不存在则添加并赋值递增的索引
const keywordIndex = (index) => keywordIndexes[index] !== undefined
? keywordIndexes[index]
: (keywordIndexes[index] = Object.keys(keywordIndexes).length)
因为赋值表达式的返回值是赋值的那个值(好像有点绕,简单解释 a = 1;
的返回值为 1
),所以可以用这种方法来应对:
index 索引过大,导致读取 keywordIndexes 为 undefined
,则给它赋值为这个对象的键的数量。如果可以读取得到,则使用它原本的键所对应的值
心得总结
JavaScript
的基础知识尤为重要,对象是很好处理复杂数据的办法,清楚 a = 1;
的返回值为 1
是解决问题的关键。