浏览代码

fix: [Select] Add key to the content returned by renderSelectedItem #320

chenyuling 3 年之前
父节点
当前提交
95c7eda1fa

+ 64 - 79
content/input/select/index-en-US.md

@@ -165,10 +165,10 @@ import { Select } from '@douyinfe/semi-ui';
     ];
     return (
         <Select placeholder="" style={{ width: 180 }} filter>
-            {data.map(group => (
-                <Select.OptGroup label={group.label} key={group.label}>
-                    {group.children.map(option => (
-                        <Select.Option key={option.value} value={option.value}>
+            {data.map((group, index) => (
+                <Select.OptGroup label={group.label} key={`${index}-${group.label}`}>
+                    {group.children.map((option, index2) => (
+                        <Select.Option value={option.value} key={`${index2}-${group.label}`}>
                             {option.label}
                         </Select.Option>
                     ))}
@@ -713,32 +713,24 @@ But you can customize the rendering of the selection box through the `renderSele
 import React from 'react';
 import { Select, Avatar, Tag } from '@douyinfe/semi-ui';
 
-class CustomRender extends React.Component {
-
-    constructor() {
-        super();
-        this.state = {
-            list: [
-                { "name": "XiaKeMan", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg"},
-                { "name": "ShenYue", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg"},
-                { "name": "QuChenYi", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg"},
-                { "name": "WenJiaMao", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png"},
-            ]
-        };
-    }
-
-    renderSelectedItem(optionNode) {
-        return (
-            <div key={optionNode.email} style={{display: 'flex', alignItems: 'center'}}>
-                <Avatar src={optionNode.avatar} size="small">{optionNode.abbr}</Avatar>
-                <span style={{ marginLeft: 8 }}>{optionNode.email}</span>
-            </div>
-        );
-    }
+() => {
+    const list = [
+        { "name": "Keman Xia", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg" },
+        { "name": "Yue Shen", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg" },
+        { "name": "Chenyi Qu", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg" },
+        { "name": "Jiamao Wen", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png" },
+    ]
+
+    const renderSelectedItem = optionNode => (
+        <div key={optionNode.email} style={{ display: 'flex', alignItems: 'center' }}>
+            <Avatar src={optionNode.avatar} size="small">{optionNode.abbr}</Avatar>
+            <span style={{ marginLeft: 8 }}>{optionNode.email}</span>
+        </div>
+    )
 
-    // avatarSrc & avatarShape are supported after 1.6.0
-    renderMultipleWithCustomTag(optionNode, { onClose }) {
-        let content = (
+    // avatarSrc & avatarShape are supported after 1.6.0-beta
+    const renderMultipleWithCustomTag = (optionNode, { onClose }) => {
+        const content = (
             <Tag
                 avatarSrc={optionNode.avatar}
                 avatarShape='circle'
@@ -756,8 +748,8 @@ class CustomRender extends React.Component {
         };
     }
 
-    renderMultipleWithCustomTag2(optionNode, { onClose }) {
-        let content = (
+    const renderMultipleWithCustomTag2 = (optionNode, { onClose }) => {
+        const content = (
             <Tag
                 avatarSrc={optionNode.avatar}
                 avatarShape='square'
@@ -775,67 +767,60 @@ class CustomRender extends React.Component {
         };
     }
 
-    renderCustomOption(item) {
-        let optionStyle = {
+    const renderCustomOption = (item, index) => {
+        const optionStyle = {
             display: 'flex',
             paddingLeft: 24,
             paddingTop: 10,
             paddingBottom: 10
-        };
+        }
         return (
-            <Select.Option value={item.name} style={optionStyle} showTick={true}  {...item} key={item.email}>
+            <Select.Option key={index} value={item.name} style={optionStyle} showTick={true}  {...item} key={item.email}>
                 <Avatar size="small" src={item.avatar} />
                 <div style={{ marginLeft: 8 }}>
                     <div style={{ fontSize: 14 }}>{item.name}</div>
-                    <div style={{ color: 'var(--semi-color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
+                    <div style={{ color: 'var(--color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
                 </div>
             </Select.Option>
-        );
+        )
     }
 
-    render() {
-        const { list } = this.state;
-        return (
-            <React.Fragment>
-                <Select
-                    style={{ width: 280, height: 40 }}
-                    onChange={v=>console.log(v)}
-                    defaultValue={'XiaKeMan'}
-                    renderSelectedItem={this.renderSelectedItem}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-                <Select
-                    maxTagCount={2}
-                    style={{width: 280, marginTop: 20}}
-                    onChange={v=>console.log(v)}
-                    defaultValue={['XiaKeMan', 'ShenYue']}
-                    multiple
-                    renderSelectedItem={this.renderMultipleWithCustomTag}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-                <Select
-                    maxTagCount={2}
-                    style={{width: 280, marginTop: 20}}
-                    onChange={v=>console.log(v)}
-                    defaultValue={['XiaKeMan', 'ShenYue']}
-                    multiple
-                    renderSelectedItem={this.renderMultipleWithCustomTag2}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-            </React.Fragment>
-        );
-    }
+    return (
+        <>
+            <Select
+                placeholder='Please select...'
+                style={{ width: 280, height: 40 }}
+                onChange={v => console.log(v)}
+                defaultValue={'Keman Xia'}
+                renderSelectedItem={renderSelectedItem}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+            <Select
+                placeholder='Please select...'
+                maxTagCount={2}
+                style={{ width: 280, marginTop: 20 }}
+                onChange={v => console.log(v)}
+                defaultValue={['Keman Xia', 'Yue Shen']}
+                multiple
+                renderSelectedItem={renderMultipleWithCustomTag}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+            <Select
+                placeholder='Please select...'
+                maxTagCount={2}
+                style={{ width: 280, marginTop: 20 }}
+                onChange={v => console.log(v)}
+                defaultValue={['Keman Xia', 'Yue Shen']}
+                multiple
+                renderSelectedItem={renderMultipleWithCustomTag2}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+        </>
+    );
 }
-
 ```
 
 ### Custom pop-up layer style

+ 64 - 83
content/input/select/index.md

@@ -177,11 +177,11 @@ import { Select } from '@douyinfe/semi-ui';
     return (
         <Select placeholder="" style={{ width: 180 }} filter>
             {
-                data.map(group => (
-                    <Select.OptGroup label={group.label} key={group.label}>
+                data.map((group, index) => (
+                    <Select.OptGroup label={group.label} key={`${index}-${group.label}`}>
                         {
-                            group.children.map(option => (
-                                <Select.Option value={option.value} key={option.value}>
+                            group.children.map((option, index2) => (
+                                <Select.Option value={option.value} key={`${index2}-${group.label}`}>
                                     {option.label}
                                 </Select.Option>
                             ))
@@ -768,39 +768,30 @@ import { Select } from '@douyinfe/semi-ui';
 import React from 'react';
 import { Select, Avatar, Tag } from '@douyinfe/semi-ui';
 
-class CustomRender extends React.Component {
-
-    constructor() {
-        super();
-        this.state = {
-            list: [
-                { "name": "夏可漫", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg"},
-                { "name": "申悦", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg"},
-                { "name": "曲晨一", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg"},
-                { "name": "文嘉茂", "email": "[email protected]", "avatar":  "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png"},
-            ]
-        };
-    }
-
-    renderSelectedItem(optionNode) {
-        return (
-            <div key={optionNode.email} style={{display: 'flex', alignItems: 'center'}}>
-                <Avatar src={optionNode.avatar} size="small">{optionNode.abbr}</Avatar>
-                <span style={{ marginLeft: 8 }}>{optionNode.email}</span>
-            </div>
-        );
-    }
+() => {
+    const list = [
+        { "name": "夏可漫", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/avatarDemo.jpeg" },
+        { "name": "申悦", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/bf8647bffab13c38772c9ff94bf91a9d.jpg" },
+        { "name": "曲晨一", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/8bd8224511db085ed74fea37205aede5.jpg" },
+        { "name": "文嘉茂", "email": "[email protected]", "avatar": "https://sf6-cdn-tos.douyinstatic.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/6fbafc2d-e3e6-4cff-a1e2-17709c680624.png" },
+    ]
+
+    const renderSelectedItem = optionNode => (
+        <div style={{ display: 'flex', alignItems: 'center' }}>
+            <Avatar src={optionNode.avatar} size="small">{optionNode.abbr}</Avatar>
+            <span style={{ marginLeft: 8 }}>{optionNode.email}</span>
+        </div>
+    )
 
-    // avatarSrc & avatarShape are supported after 1.6.0
-    renderMultipleWithCustomTag(optionNode, { onClose }) {
-        let content = (
+    // avatarSrc & avatarShape are supported after 1.6.0-beta
+    const renderMultipleWithCustomTag = (optionNode, { onClose }) => {
+        const content = (
             <Tag
                 avatarSrc={optionNode.avatar}
                 avatarShape='circle'
                 closable={true}
                 onClose={onClose}
                 size='large'
-                key={optionNode.name}
             >
                 {optionNode.name}
             </Tag>
@@ -811,15 +802,14 @@ class CustomRender extends React.Component {
         };
     }
 
-    renderMultipleWithCustomTag2(optionNode, { onClose }) {
-        let content = (
+    const renderMultipleWithCustomTag2 = (optionNode, { onClose }) => {
+        const content = (
             <Tag
                 avatarSrc={optionNode.avatar}
                 avatarShape='square'
                 closable={true}
                 onClose={onClose}
                 size='large'
-                key={optionNode.name}
             >
                 {optionNode.name}
             </Tag>
@@ -830,68 +820,59 @@ class CustomRender extends React.Component {
         };
     }
 
-    renderCustomOption(item) {
-        let optionStyle = {
+    const renderCustomOption = (item, index) => {
+        const optionStyle = {
             display: 'flex',
             paddingLeft: 24,
             paddingTop: 10,
             paddingBottom: 10
-        };
+        }
         return (
-            <Select.Option value={item.name} style={optionStyle} showTick={true}  {...item} key={item.email}>
+            <Select.Option key={index} value={item.name} style={optionStyle} showTick={true}  {...item} key={item.email}>
                 <Avatar size="small" src={item.avatar} />
                 <div style={{ marginLeft: 8 }}>
                     <div style={{ fontSize: 14 }}>{item.name}</div>
-                    <div style={{ color: 'var(--semi-color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
+                    <div style={{ color: 'var(--color-text-2)', fontSize: 12, lineHeight: '16px', fontWeight: 'normal' }}>{item.email}</div>
                 </div>
             </Select.Option>
-        );
+        )
     }
 
-    render() {
-        const { list } = this.state;
-        return (
-            <React.Fragment>
-                <Select
-                    placeholder='请选择'
-                    style={{ width: 280, height: 40 }}
-                    onChange={v=>console.log(v)}
-                    defaultValue={'夏可漫'}
-                    renderSelectedItem={this.renderSelectedItem}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-                <Select
-                    placeholder='请选择'
-                    maxTagCount={2}
-                    style={{width: 280, marginTop: 20}}
-                    onChange={v=>console.log(v)}
-                    defaultValue={['夏可漫', '申悦']}
-                    multiple
-                    renderSelectedItem={this.renderMultipleWithCustomTag}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-                <Select
-                    placeholder='请选择'
-                    maxTagCount={2}
-                    style={{width: 280, marginTop: 20}}
-                    onChange={v=>console.log(v)}
-                    defaultValue={['夏可漫', '申悦']}
-                    multiple
-                    renderSelectedItem={this.renderMultipleWithCustomTag2}
-                >
-                    {
-                        list.map(item => this.renderCustomOption(item))
-                    }
-                </Select>
-            </React.Fragment>
-        );
-    }
+    return (
+        <>
+            <Select
+                placeholder='请选择'
+                style={{ width: 280, height: 40 }}
+                onChange={v => console.log(v)}
+                defaultValue={'夏可漫'}
+                renderSelectedItem={renderSelectedItem}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+            <Select
+                placeholder='请选择'
+                maxTagCount={2}
+                style={{ width: 280, marginTop: 20 }}
+                onChange={v => console.log(v)}
+                defaultValue={['夏可漫', '申悦']}
+                multiple
+                renderSelectedItem={renderMultipleWithCustomTag}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+            <Select
+                placeholder='请选择'
+                maxTagCount={2}
+                style={{ width: 280, marginTop: 20 }}
+                onChange={v => console.log(v)}
+                defaultValue={['夏可漫', '申悦']}
+                multiple
+                renderSelectedItem={renderMultipleWithCustomTag2}
+            >
+                {list.map((item, index) => renderCustomOption(item, index))}
+            </Select>
+        </>
+    );
 }
 ```
 

+ 14 - 14
packages/semi-ui/select/_story/select.stories.js

@@ -1033,12 +1033,12 @@ class CustomRender extends React.Component {
     };
   }
 
-  renderCustomOption(item) {
-    let optionStyle = {
+  renderCustomOption(item, index) {
+    const optionStyle = {
       display: 'flex',
     };
     return (
-      <Option value={item.name} style={optionStyle} showTick={false} {...item}>
+      <Option key={index} value={item.name} style={optionStyle} showTick={false} {...item}>
         <Avatar color={item.color} size="small">
           {item.abbr}
         </Avatar>
@@ -1098,7 +1098,7 @@ class CustomRender extends React.Component {
     };
   }
 
-  renderMultipleWithoutTag(optionNode, { onClose, index }) {
+  renderMultipleWithoutTag(optionNode) {
     let content = (
       <div>
         <Avatar color={optionNode.color} size="small">
@@ -1125,7 +1125,7 @@ class CustomRender extends React.Component {
           defaultValue={'夏可漫'}
           renderSelectedItem={this.renderSelectedItem}
         >
-          {list.map(item => this.renderCustomOption(item))}
+          {list.map((item, index) => this.renderCustomOption(item, index))}
         </Select>
         <Select
           style={{
@@ -1138,7 +1138,7 @@ class CustomRender extends React.Component {
           multiple
           renderSelectedItem={this.renderMultipleSelectedItem}
         >
-          {list.map(item => this.renderCustomOption(item))}
+          {list.map((item, index) => this.renderCustomOption(item, index))}
         </Select>
         <Select
           style={{
@@ -1151,7 +1151,7 @@ class CustomRender extends React.Component {
           multiple
           renderSelectedItem={this.renderMultipleWithoutTag}
         >
-          {list.map(item => this.renderCustomOption(item))}
+          {list.map((item, index) => this.renderCustomOption(item, index))}
         </Select>
       </React.Fragment>
     );
@@ -2122,15 +2122,15 @@ class OptionGroupDemo extends React.Component {
     });
   }
 
-  renderGroup(group) {
-    let options = group.children.map(option => (
-      <Select.Option value={option.value} label={option.label} key={option.label}></Select.Option>
+  renderGroup(group, index) {
+    const options = group.children.map(option => (
+      <Select.Option value={option.value} label={option.label} key={option.label} />
     ));
-    return <Select.OptGroup label={group.label}>{options}</Select.OptGroup>;
+    return <Select.OptGroup key={`${index}-${group.label}`} label={group.label}>{options}</Select.OptGroup>;
   }
 
   render() {
-    let { groups } = this.state;
+    const { groups } = this.state;
     return (
       <>
         <Select
@@ -2142,14 +2142,14 @@ class OptionGroupDemo extends React.Component {
           onSearch={this.handleSearch}
           remote
         >
-          {groups.map(group => this.renderGroup(group))}
+          {groups.map((group, index) => this.renderGroup(group, index))}
         </Select>
       </>
     );
   }
 }
 
-export const SelectOptionGroup = () => <OptionGroupDemo></OptionGroupDemo>;
+export const SelectOptionGroup = () => <OptionGroupDemo />;
 
 SelectOptionGroup.story = {
   name: 'Select OptionGroup',

+ 1 - 1
packages/semi-ui/select/index.tsx

@@ -882,7 +882,7 @@ class Select extends BaseComponent<SelectProps, SelectState> {
                     </Tag>
                 );
             } else {
-                return content;
+                return <Fragment key={value}>{content}</Fragment>;
             }
         });