I tried react+ TS + AntDesign for the first time. I did a project with React + TS + AntDesign, and recorded all the pits for beginners. I had to marvel how easy React Hook was to use. It seems to take more than a few words to show up in a thumbnail, but my compulsion forces me to add a lot of tick-tock

Antdesign nested subtables to get asynchronous data

1. Preliminary program: inexpandedRowRenderIn the request

// Subform section
  const expandedRowRender = (record: detailParams) = > {
    let {errorMsg, errorOther, errorType} = record
    let data = {
      projectName,
      errorMsg,
      errorOther,
      errorType
    }
    
    getErrorListDetail(data).then((da: any) = > {
      if (da.bizSuccess) {
        da.dataList.forEach((v: any, index: number) = > {
          v.key = index
        })
        setSubDataSource(da.dataList)
      } else {
        setError(da.msg || 'Failed to get data')
      }
    }).catch(err= > {
      setError(err)
    })
    
    const columns = [
      {
        title: 'Error type'.dataIndex: 'errorType'.key: 'errorType'.className: 'column-center'.width: 200}]return <div>
      {error ? (<Alert message={error} type="success" closable afterClose={()= > setError(null)} />) : null}
      
      <Table columns={columns} dataSource={subDataSource} pagination={false} />
    </div>
  };
Copy the code

Existing problems

After clicking once to load a subtable, requests continue to be made, creating an endless loop

why

ExpandedRowRender is called in the Render method of the Table component. React Render calls getErrorListDetail asynchronously cause repeated calls. GetErrorListDetail -> setState -> render -> getErrorListDetail -> setState -> render

2. Optimization scheme

Write the request in onExpend and render in expandedRowRender

const onExpand = (expanded: boolean, record: detailParams) = > {
    if(! expanded) {// If you keep adding key-value pairs, it will cause too much data and waste resources.
      // Empty the data under the corresponding key at each merge
    } else {      
      let {errorMsg, errorOther, errorType} = record
      let data = {
        projectName,
        errorMsg,
        errorOther,
        errorType
      }

     getErrorListDetail(data).then((da: any) = > {
        if (da.bizSuccess) {
          da.dataList.forEach((v: any, index: number) = > {
            v.key = index
          })
          setSubDataSource(da.dataList)
        } else {
          setError(da.msg || 'Failed to get data')
        }
      }).catch(err= > {
        setError(err)
      })
    }
 }
Copy the code

Existing problems

Each time you click expand to request data for the current row, you modify nested subform data for other rows

why

3. Final plan

Give each row a key: rowKey={record => record.key}

const onExpand = (expanded: boolean, record: detailParams) = > {
    if(! expanded) {// If you keep adding key-value pairs, it will cause too much data and waste resources.
      // Empty the data under the corresponding key at each merge
      constdata = { ... subDataSourceObj, [record.key]: [] } setSubDataSourceObj(data) }else {      
      let {errorMsg, errorOther, errorType} = record
      let data = {
        projectName,
        errorMsg,
        errorOther,
        errorType
      }

      getErrorListDetail(data).then((da: any) = > {        
        const {bizSuccess, dataList, msg} = da
        if (bizSuccess) {
          dataList.forEach((v: any, index: number) = > {
            v.key = index
          })
          constdata = { ... subDataSourceObj, [record.key]: dataList } setSubDataSourceObj(data) }else {
          setError(msg || 'Failed to get data')
        }
      }).catch(err= > {
        setError(err)
      })
    }
  }
Copy the code

SubDataSourceObj [record.key] is used to display subtables

antdesignDate component, which defaults to English international

Chinese version is required according to project requirements

import moment from 'moment'
import 'moment/locale/zh-cn'
// Put it at the end of all introductions
moment.locale('zh-cn')

<RangePicker
    // Set it to Chinese
    locale={zhCN}
    showTime={{ format: 'HH:mm' }}
    format="YYYY-MM-DD HH:mm"
    onChange={(e, value) = > {onTimeChange(value)}}
  />
Copy the code

The moment and ANTD dependencies installed with NPM will only become Chinese, so you need to install them with YARN add

Run the yarn add antd moment command. The problem is resolved

About English and Chinese

  • Antdesign is also in English by default, you need to add Chinese conversion to the index.tsx file
import cn from 'antd/es/locale/zh_CN';

ReactDOM.render( 
  <ConfigProvider locale={cn}>
      <App />
  </ConfigProvider>.document.getElementById('root'));Copy the code

Path problems after the project is packaged

1. The packed static resources cannot be loaded. They are all 404

  • The solution
    • inpackage.jsonAdd a line of code to:"homepage": "."

2. Update 404 in the production environment

  • why
    • Using BroswerRouter to refresh in a production environment can be problematic
  • The solution
    • To switch to hashHistory

Although browserHistory does a lot more, browserHistory uses HTML5’s History API, and the browser provides an interface to modify the browser’s History. HashHistory changes the browser’s history by changing the hash after the address; The History API provides pushState() and replaceState() methods to add or replace History. Hash has no method, so there is no way to replace history. But the React-Router implements this function with polyfill, as if using sessionStorage.

Another reason is that the hash part is not sent to the server by the browser, meaning that whether #foo or #bar is requested, the service only knows that the index.html is requested and does not know the hash part’s details. The History API requires server support so that the server can retrieve request details. Another reason is that some should ignore the hash part of the URL. Remember that the hash part will be lost when the URL is shared using wechat before.

But we can use hashHistory without needing too much functionality

React Hook basics notes

State of ascension

  • In React, any mutable data is supposed to have a single data source, keeping the data flow top-down in the application
  • Multiple components share the same data, which needs to be promoted to the nearest parent component for management
  • And bidirectional data flow
    • Instead of trying to synchronize state across different components, you synthesize the data at the data source

react hook

effect hook

  • Side effects that do not require clearance
UseEffect is executed after each render when it is a pure function
useEffect(() = > {})
Copy the code
  • Side effects that need to be removed
    // Return function, can be cleared
    // Clear the click event
    useEffect(() = > {
        document.addEventListener('click'.console.log(1))
        return () = > {
            document.removeEventListener('click')}})Copy the code

Control effect execution (second parameter)

UseEffect When the second argument is an empty array, it is executed only once
useEffect(() = > {}, [])

UseEffect () {useEffect () {useEffect ();
useEffect(() = > {
	console.log(like)
}, [like])
Copy the code

HOC versus the pitfalls of custom hooks

HOC: Higher Order Component

The argument is a component, and the return value is a component

Customize the hook

Multiple calls (requests, etc.) can be wrapped into a function and extracted separately

useRef

  • Changing the value of ref does not cause the render of the component
const likeRef = useRef(0)
console.log(likeRef.current)
Copy the code
  • Common usage — get a DOM node

useContext

A way to share values between ancestor and descendant components, and between sibling components (to avoid layer by layer in the component tree)

Rules of the hook

  • Use hooks only at the top level, not in loops or nested functions
  • You can only call a hook in a react function or custom hook, not a js function

Other Hook

usehooks.com/

  • The useReducer is used to transfer values to components
  • UseCallback is used for performance tuning