从0开发React项目-14-Money.tsx组件功能

121 阅读2分钟

React18 函数组件的定义,并React16更规范

const X:React.FunctionComponent = () => {
    return (
        <div></div>
    );
}

或者

const X:React.FC = () => {
    return (
        <div></div>
    );
}

想用props.children

const TT: React.FC = (props) => {
    return (<div>{props.children}</div>);
}

遇到报错 Property 'children' does not exist on type '{}'.
这是因为react 18 更严格,给props加上any类型,不报错

const TT: React.FC = (props: any) => {
    return (<div>{props.children}</div>);
}

可是外部使用时,

return (
    <TT>
        <div>hi</div>
    </TT>
);

还是报错,似乎不应该传一个children
Type '{ children: Element[]; }' has no properties in common with type 'IntrinsicAttributes'
谷歌得到: It seems the problem comes from the react-18 update in how React.FC is typed, as doesn't include children anymore 这样改才行

const TT: React.FC<{ children: React.ReactNode }> = (props: any) => {
    return (<div>{props.children}</div>);
}

或者

type Props = {
    children?: React.ReactNode;
}
const TT: React.FC<Props> = (props: any) => {
    return (<div>{props.children}</div>);
}

这个children在react16还是默认就带的,在react18中就要显示声明,
参考:stackoverflow.com/questions/7…

标签组件

这样写是错误的

const TagsSection: React.FunctionComponent<Props> = () => {
    const [tags, setTags] = useState<string[]>(['衣', '食', '住', '行']);
    return (
        <Wrapper>
            <ol>
                {tags.map((tag) => {
                    <li key={tag}>{tag}</li>
                })}
            </ol>
            <button>新增标签</button>
        </Wrapper>
    );
}

这样写才行

<ol>
    {tags.map((tag) =>
        <li key={tag}>{tag}</li>
    )}
</ol>

或者这样写

<ol>
    {tags.map((tag) => {
        return <li key={tag}>{tag}</li>
    })}
</ol>

一切都是因为标签中数组的遍历方法,箭头函数后年的大括号就不能要,要了除非加return返回

严格模式的一个问题

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

严格模式会导致函数组件执行两次,不用严格模式

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

usrRef的使用

const inputRef = useRef<HTMLInputElement>(null);,其中,类型HTMLInputElement是必须的

input,受控组件,非受控组件

受控组件的写法,通过valueonChange控制

const NotesSection: React.FunctionComponent<Props> = (props) => {
    const [notes, setNotes] = useState('');
    return (<Wrapper>
        <label>
            <span>备注</span>
            <input type="text"
                   placeholder={"在这里添加备注"}
                   defaultValue={notes}
                   onBlur={(e) => {
                       setNotes(e.target.value);
                   }}
            />
        </label>
    </Wrapper>);
}

非受控组件的写法,通过defaultValueonBlur获取组件的值

const NotesSection: React.FunctionComponent<Props> = (props) => {
    const [notes, setNotes] = useState('');
    const inputRef = useRef<HTMLInputElement>(null);
    return (<Wrapper>
        <label>
            <span>备注</span>
            <input type="text"
                   ref={inputRef}
                   placeholder={"在这里添加备注"}
                   defaultValue={notes}
                   onBlur={(e) => {
                       // setNotes(e.target.value);
                       if(inputRef && inputRef.current){
                           setNotes(inputRef.current.value);
                       }
                   }}
            />
        </label>
    </Wrapper>);
}

字符串联合类型

('-'|'+')[]表示字符串局限于-或者+两个字符,注意,是|
const [categoryList, setCategoryList] = useState<('-'|'+')[]>(['-', '+']); 完整代码如下

const CategorySection: React.FunctionComponent<Props> = () => {
    const categoryMap = { '-': '支出', '+': '收入' }
    const [category, setCategory] = useState<(keyof typeof categoryMap)>('-'); // 保存当前状态的
    const [categoryList, setCategoryList] = useState<(keyof typeof categoryMap)[]>(['-', '+']);
    return (<Wrapper>
        <ul>
            {categoryList.map((c) =>
                <li key={c}
                    className={category == c ? "selected" : ""}
                    onClick={() => {
                        setCategory(c);
                    }}>{categoryMap[c]}
                </li>)}
        </ul>
    </Wrapper>);
}

其他

  • 淘宝橙:#f60
  • React和HTML的onChange事件是不一样的,HTML的onChange会再鼠标移走、onBlur之前的时候触发,而HTML的onChange会在输入一个字符时触发
  • 数组删除最后一个字符outPut.slice(0,-1)
  • 部分类型Partial
const [selected, setSelected] = useState({
    tags: [] as string[],
    note: '',
    category: '-' as Category,
    amount: 0
});
const onChange = (obj: Partial<typeof selected>) => {
    setSelected({
        ...selected,
        ...obj
    });
}