--- robots: noindex, nofollow tags: React --- # React 元件重構實務 ## 多選列表 趕工時常常會寫出很隨便的 UI 元件,例如當你需要一個多選標籤時,可能會這樣寫: ```jsx const Tag = styled.div` border: solid 1px gray; text-align: justify; display: flex; justify-content: center; align-items: center; cursor: pointer; margin-right: 8px; `; const List = styled.div` display: flex; justify-content: left; align-items: center; flex-wrap: wrap; `; const TagList = () => ( <List> <Tag>orange</Tag> <Tag>apple</Tag> <Tag>banana</Tag> </List> ); ``` 但是你知道,一個列表該怎麼排版,是由父元素決定的,所以你會這樣改寫: ```jsx= const Tag = styled.div` border: solid 1px gray; text-align: justify; display: flex; justify-content: center; align-items: center; cursor: pointer; `; const List = styled.div` display: flex; justify-content: left; align-items: center; flex-wrap: wrap; ${Tag} { margin-right: 8px; &:last-child { margin-right: 0; } } `; ``` 接著你希望可以讓資料決定這個列表長怎樣: ```jsx= const TagList = ({ tags = [] }) => { return ( <List> {tags.map((tag) => <Tag key={tag.id}>{tag.name}</Tag> )} </List> ); }; ``` 然後讓這個列表可以保存目前的狀態: ```jsx= const TagList = ({ tags = [], onTagClick = () => void }) => { return ( <List> {tags.map((tag) => <Tag key={tag.id} actived={tag.isSelect} onClick={() => onTagClick(tag)} > {tag.name} </Tag> )} </List> ); }; ``` 但是你也知道,像 `isSelect` 這樣的 UI 狀態,對整個應用程式來說是多餘的,所以想把它藏起來: ```jsx= const TagList = ({ tags = [], candidates = [], onChange = () => void }) => { const tagMap = useMemo( () => { let result = {}; for (let t of tags) { result[t.id] = true; } return result; }, [tags], ); return ( <List> {candidates.map((tag) => <Tag key={tag.id} actived={!!tagMap[tag.id]} onClick={() => { let result = []; // 保證輸出為新陣列,且和 candidates 的順序一致 for (let t of candidates) { const checked = tagMap[t.id]; if (tag.id === t.id) { if (!checked) result.push(t); } else { if (checked) result.push(t); } } onChange(result); }} > {tag.name} </Tag> )} </List> ); }; ``` 最後你得到一個可以這樣用的列表: ```jsx= const Parent = ({ allTags = [] }) => { const [tags, setTags] = useState([]); return ( <TagList tags={tags} candidates={allTags} onChange={setTags} /> ); }; ``` ## 抽離副作用 (待續) ## 巢狀 Route (待續)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up