tags页面数据持久化

使用useEffect获取和存储tags到localstorage

  1. const useTags = () => {
  2. const [tags, setTags] = useState<{id: number, name: string}[]>([])
  3. useEffect(()=>{
  4. let localTags = (JSON.parse(window.localStorage.getItem('tags') || '[]'))
  5. if(localTags.length === 0) {
  6. localTags = [
  7. {id: createId(), name: '衣'},
  8. {id: createId(), name: '食'},
  9. {id: createId(), name: '住'},
  10. {id: createId(), name: '行'}]
  11. }
  12. setTags(localTags)
  13. },[])
  14. useEffect(()=>{
  15. window.localStorage.setItem('tags', JSON.stringify(tags))
  16. },[tags])
  17. // ...
  18. }

注意,这样写会导致setItem执行2次,一次是tags从undefined变成[], 第二次是tags从[]变成['衣','食','住','行']
使用count计算渲染的次数,让第一次渲染时不执行setItem
注意:当你希望一个变量在组件不断render时保持不变,使用useRef

  1. let count = useRef(0)
  2. useEffect(()=>{
  3. count.current += 1
  4. })
  5. useEffect(()=>{
  6. if(count.current > 1) {
  7. window.localStorage.setItem('tags', JSON.stringify(tags))
  8. }
  9. },[tags])

封装成useUpdate hooks

  1. import {useEffect, useRef} from 'react';
  2. export const useUpdate = (fn: ()=>void, deps: any[]) => {
  3. const count = useRef(0)
  4. useEffect(()=>{
  5. count.current += 1
  6. })
  7. useEffect(()=>{
  8. if(count.current > 1) {
  9. fn()
  10. }
  11. },deps)
  12. }

修改createId

  1. let id = parseInt(window.localStorage.getItem('idMax') || '0')
  2. const createId = () => {
  3. id += 1;
  4. window.localStorage.setItem('idMax', id.toString())
  5. return id;
  6. }
  7. export {createId}

useEffect的执行时机

每个使用useTags的组件,在刷新时都会重新渲染,重新获取tags的状态
useEffect(()=>{//do something}, [])是在componentDidMount周期运行的
useState在Mount前运行,此时tags为空数组,所以在tags组件中要判断tag是否为空

  1. const Tag: React.FC = () => {
  2. const history = useHistory();
  3. const {findTag, updateTag, deleteTag} = useTags();
  4. const {id} = useParams<Params>();
  5. const tag = findTag(parseInt(id));
  6. const tagContent = (tag:{id: number, name: string}) => (
  7. <>
  8. <InputWrapper>
  9. <Input label="标签名"
  10. type="text"
  11. placeholder="标签名"
  12. value={tag.name}
  13. onChange={(e) => updateTag(tag.id, e.target.value)}
  14. />
  15. </InputWrapper>
  16. <Center>
  17. <Space/>
  18. <Space/>
  19. <Space/>
  20. <Button onClick={() => {
  21. const result = deleteTag(tag.id);
  22. if(result === 'success')
  23. history.goBack();
  24. }}>删除标签</Button>
  25. </Center>
  26. </>
  27. )
  28. return (
  29. <Layout>
  30. <TopBar>
  31. <Icon name="left" onClick={()=> history.goBack()}/>
  32. <span>编辑标签</span>
  33. <Icon/>
  34. </TopBar>
  35. {tag ? tagContent(tag) : ''}
  36. </Layout>
  37. );
  38. };

Money页面数据持久化

useRecords

  1. import {useEffect, useState} from 'react';
  2. import {useUpdate} from './useUpdate';
  3. type RecordItem = {
  4. tagIds: number[],
  5. category: '-' | '+',
  6. note: string,
  7. amount: number,
  8. createAt: string
  9. }
  10. type newRecordItem = Omit<RecordItem, 'createAt'>
  11. export const useRecords = () => {
  12. const [records, setRecords] = useState<newRecordItem[]>([])
  13. useEffect(()=>{
  14. setRecords(JSON.parse(window.localStorage.getItem('records') || '[]'))
  15. }, [])
  16. const addRecord = (record: newRecordItem) => {
  17. if(record.amount <= 0) {
  18. alert('请输入金额')
  19. return false
  20. }
  21. if(record.tagIds.length === 0) {
  22. alert('请选择标签')
  23. return false
  24. }
  25. const newRecord:RecordItem = {...record, createAt: new Date().toISOString()}
  26. setRecords([...records, newRecord])
  27. return true
  28. }
  29. useUpdate(()=>{
  30. window.localStorage.setItem('records', JSON.stringify(records))
  31. }, [records])
  32. return {
  33. records,
  34. addRecord,
  35. }
  36. }