Icon组件name修改为可选

  1. type Props = {
  2. name?: string
  3. }
  4. const Icon = (props: Props) => {
  5. return <svg className="icon">
  6. {props.name && <use xlinkHref={'#'+props.name}/>}
  7. </svg>
  8. }

抽离Input组件

  1. import styled from 'styled-components';
  2. import React from 'react';
  3. const Label = styled.label`
  4. display: flex;
  5. align-items: center;
  6. > span {
  7. margin-right: 16px; white-space: nowrap;
  8. }
  9. > input {
  10. display: block;
  11. height: 72px;
  12. width: 100%;
  13. background: none;
  14. border: none;
  15. }
  16. `;
  17. type Props = {
  18. label: string;
  19. } & React.InputHTMLAttributes<HTMLInputElement> // 继承input的所有属性
  20. const Input: React.FC<Props> = (props) => {
  21. const {label, children, ...rest} = props;
  22. return (
  23. <Label>
  24. <span>{props.label}</span>
  25. <input {...rest} />
  26. </Label>
  27. );
  28. };
  29. export {Input};

使用Input

  1. const NoteSection: React.FC<Props> = (props) => {
  2. const note = props.value
  3. const onChange: ChangeEventHandler<HTMLInputElement> = (e) => {
  4. props.onChange(e.target.value);
  5. }
  6. return (
  7. <Wrapper>
  8. <Input label="备注" type="text" placeholder="在这里输入备注" defaultValue={note} onChange={onChange} />
  9. </Wrapper>
  10. )
  11. }

编辑标签功能

updateTag

  1. const updateTag = (id:number, name: string) => {
  2. const tag = {id,name}
  3. const newTags = JSON.parse(JSON.stringify(tags))
  4. let index = -1
  5. for (let i = 0; i<newTags.length; i++){
  6. if(newTags[i].id === id) {
  7. index = i;
  8. break;
  9. }
  10. }
  11. if(index >= 0){
  12. newTags.splice(index, 1, tag)
  13. }
  14. setTags(newTags)
  15. }

React不提倡直接修改源数据
setState时要传一个全新的对象才能刷新页面

更优雅的更新和删除tag

  1. const updateTag = (id:number, name: string) => {
  2. setTags(tags.map(tag => tag.id === id ? {id, name} : tag))
  3. }
  4. const deleteTag = (id:number) => {
  5. setTags(tags.filter(tag => tag.id !== id))
  6. }

useHistory

使用useHistory控制路由的回退,前进,或跳转到指定路径

  1. import {history} from 'react-router-dom'
  2. const history = useHistory()
  3. history.goBack() // 回退
  4. history.push('/') // 跳转到指定路径

使用window.history.back()也一样能实现跳转,相当于点击浏览器的回退按钮

回退时页面会刷新吗?不会
2种方法验证:

  1. 在控制台查看,当页面刷新时一定会有新的请求
  2. 在入口文件index.tsx中console.log, 页面刷新必然执行入口文件

让Icon支持onClick

  1. <Icon name="left" onClick={()=> history.goBack()}/>
  1. type Props = {
  2. name?: string
  3. } & React.SVGAttributes<SVGElement>
  4. const Icon = (props: Props) => {
  5. const {name, children, ...rest} = props
  6. return <svg className="icon" {...rest}>
  7. {props.name && <use xlinkHref={'#'+name}/>}
  8. </svg>
  9. }

注意如果className也在…rest里面,如果从外部传入className, 就会覆盖掉原有的className=”icon”
所有className要提出来

  1. const Icon = (props: Props) => {
  2. const {name, children, className, ...rest} = props
  3. return <svg className="icon" {...rest}>
  4. {props.name && <use xlinkHref={'#'+name}/>}
  5. </svg>
  6. }

使用classnames库自动合并classname

安装

  1. npm install classnames
  2. npm install --save-dev @types/classnames

使用

  1. import cs from 'classnames'
  2. const Icon = (props: Props) => {
  3. const {name, children, className, ...rest} = props
  4. return <svg className={cs("icon",className)} {...rest}>
  5. {props.name && <use xlinkHref={'#'+name}/>}
  6. </svg>
  7. }