Recently, I learned React and decided to implement a version with React to consolidate and practice the basic knowledge of React. The functionality of this little project is basically the same as the previous version, and the logic on each page is basically the same, as documented in my other blog post, “Implementing a simple bookkeeping book with Vue.” React Implementation Process This article mainly records some ideas and knowledge summary.

Link: Click to preview the Github source code

Need to improve

Based on my own use of the previous version, I decided to add an edit function to the statistics page for every billing record displayed, that is, add a new route to an edit page that provides a button to delete or edit that record.

Project record

1. Create and configure projects

  • To set up a project using create-react-app, perform the following steps:
Yarn Global add create-react-app // Or use NPM to install the create-react-app project name --template typescript CD Project folder YARN start // The browser automatically opensCopy the code
  • css normalize

In the index. The CSS to add

@import-normalize;
Copy the code

To ensure that the default style of the page on different browsers is similar.

  • SCSS support

According to the CRA official website, Node-sass must be installed

  • Set the use of absolute paths in create-react-app references
  1. Add it to tsconfig.json

  1. WebStorm sets the SRC directory to Resource Root

  1. Then selectSettings > Editor > Code Style > TypeScriptAnd into theImportsUnder the TAB checkUse paths relative to tsconfig.json.

Ok ~ test it:

To take effect

Project construction and basic configuration are completed. For more information, please refer to the CRA official documentation

2. Some knowledge points to use

  • React Router

First, according to the requirements and the basic usage of the document, I wrote the following code, using Redirect to the billing page as the home page of the app.

<Router>
    <Route exact path="/tags" component={Tags}></Route>
    <Route exact path="/tags/:id" component={Tag}></Route>
    <Route exact path="/money" component={Money}></Route>
    <Route exact path="/statistics" component={Statistics}></Route>
    <Route exact path="/statistics/:recordId" component={Record}></Route>
    <Route exact path="/money/:recordId/edit" component={Money}></Route>
    <Redirect exact from="/" to="/money"/>
    <Route path="*" component={NoMatch}></Route>
<Router>
Copy the code

But this led to an endless loop, and when I looked at the document again, it explained:

Finally, the routing algorithm matches the routes from top to bottom in a defined order. Therefore, when you have two sibling routing node configurations, you must ensure that the former route does not match the path in the latter route. For example, don’t:

<Route path="/comments" ... />
<Redirect from="/comments" ... />
Copy the code

That is, according to the matching principle, it will assume that every page needs to be redirected to the corresponding page of /money, creating an endless loop. The liberation method is to route the component with Switch and rewrite the code as:

<Router>
  <Switch>
    <Route exact path="/tags" component={Tags}>
    </Route>
    <Route exact path="/tags/:id" component={Tag}>
    </Route>
    <Route exact path="/money" component={Money}>
    </Route>
    <Route exact path="/statistics" component={Statistics}>
    </Route>
    <Route exact path="/statistics/:recordId" component={Record}>
    </Route>
    <Route exact path="/money/:recordId/edit" component={Money}>
    </Route>
    <Redirect exact from="/" to="/money"/>
    <Route path="*" component={NoMatch}>
    </Route>
  </Switch>
</Router>
Copy the code

No Switch component is equivalent to if else; A switch is a JS switch that matches only one. Writing routes later can save performance by writing Switch components.

Add NavLink to the navigation component to switch routes. NavLink is another version of Link, which can automatically add style attributes to the tag after matching the route. ActiveClassName defines the attribute name after matching the route.

<ul> <li> <NavLink to="/tags" activeClassName="selected"> <Icon name="tag"/> </NavLink> </li> <li> <NavLink To ="/money" activeClassName="selected"> <Icon name="money"/> </NavLink> <li> <li> <NavLink to="/statistics" ActiveClassName ="selected"> <Icon name="statistics"/>Copy the code
  • Import all SVG in batches

As in the VUE project, svG-Sprite-Loader and SvGo-Loader need to be installed and configured.

The way to introduce a single SVG is:

import x from 'icons/id.svg';

<svg>
    <use xlinkHref="#id" />
</svg>
Copy the code

However, since Tree Shaking, some unused but imported files will be removed from the final file packaged by Webpack, it can only be brought in with the require syntax, so this part of the code will not be Tree Shaking.

require('icons/id.svg');
Copy the code

In order to use SVG more quickly, I was trying to find a way to import all SVG at once without having to import them one by one. After some searching, this can be done by:

  1. runyarn ejectGenerate config folder
  2. inwebpack.config.js, found inmodule > rules > oneOf The following add
{
  test: /.svg$/,
  use: [
    { loader: 'svg-sprite-loader'.options: {}}, {loader: 'svgo-loader'.options: {
        plugins:[
          {removeAttrs: {attrs: 'fill'}} // Automatically remove the original fill color when importing icon]}}]},Copy the code
  1. Add the following to the imported SVG file:
let importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext); try { importAll(require.context('icons', true, /\.svg$/)); } catch (error) {console.log(error); }Copy the code

Once this is done, you can use the SVG name directly!

<svg>
    <use xlinkHref="#id" />
</svg>
Copy the code
  • Use of React Hooks

1. useState

const [state, setState] = useState(initialState);
Copy the code

Returns a state and a function to update the state.

During initial rendering, the state returned is the same as the value of the first parameter passed in.

The setState function is used to update state. It receives a new state value and queues a re-rendering of the component.

setState(newState);
Copy the code

In subsequent rerenders, the first value returned by useState will always be the latest state after the update.

2. useEffect

useEffect(didUpdate);
Copy the code

The Hook receives a function that contains imperative code that may have side effects.

Use useEffect for side effects. The function assigned to useEffect is executed after the component is rendered to the screen.

By default, effect will be executed at the end of each render round, but you can choose to have it executed only when certain values change.

Timing of effect execution: The function passed to useEffect is called in a delayed event after the browser has finished layout and drawing. UseEffect is guaranteed to be executed before any new rendering, although it is delayed after the browser has drawn. React always clears the effect of the last render before starting a new update.

By default, effect is executed after each round of component rendering is complete. This way, the effect will be recreated as soon as its dependency changes. To avoid some unnecessary execution, you can pass useEffect a second argument, which is the array of values that Effect depends on.

If you want to execute effect once (only when the component is mounted and unmounted), you can pass an empty array ([]) as the second argument.

Note: If you pass in the second argument, make sure the array contains all variables that will change in the outer scope and be used in effect, otherwise your code will refer to the old variables in the previous rendering.

3. useRef

const refContainer = useRef(initialValue);
Copy the code

UseRef returns a mutable ref object whose.current property is initialized as the passed parameter (initialValue). The ref object returned persists throughout the life of the component.

4. Customize Hooks

A custom Hook is a function whose name starts with “use” that can call other hooks from within.

We can extract the repetitive logic into a custom Hook, which is just like a normal function, but its name must start with use. Otherwise, there is no way to tell if a function contains calls to its internal hooks. React will not automatically check if your hooks violate the Hook rules.

UseUpdate Hook – useUpdate Hook – useUpdate Hook – useUpdate Hook – useUpdate Hook – useUpdate Hook – useUpdate Hook

import {useEffect, useRef} from 'react';

export const useUpdate = (fn:()=>void, dependency:any[]) => {
  const count = useRef(0);
  useEffect(() => {
    count.current += 1;
  })
  useEffect(() => {
    if(count.current > 1) {
      fn();
    }
  }, [fn, dependency]);
}
Copy the code

Read more about React documentation

  • React Router Hooks

1. useHistory

UseHistory provides access to history instances that can be used for navigation.

2. useParams

UseParams returns the key/value pair object of the URL parameter. Use it to access the current mate.params.

Read more about the React Router documentation

  • The data in React is immutable

React stresses that data cannot be changed. Data can only be updated by reassigning values.

Benefits of immutability:

  1. Simplify complex features: Immutability makes complex features easier to implement.
  2. Tracking data changes: It is difficult to track data changes if you modify the data directly. Tracking data changes requires that the mutable object can be compared to the previous version, so that the entire object tree needs to be traversed once. It is relatively easy to track changes in immutable data. If the object becomes a new object, we can say that the object has changed.
  3. Determine when to re-render in React: The main advantage of immutability is that it helps us create in Reactpure components. We can easily determine if immutable data has changed and thus when to rerender the component.
  • Data management

Local data is stored in localStorage, and the operation API of data is encapsulated by custom Hook. Each component can obtain the operation API of data through custom Hook.

conclusion

This is a simple record of my bookkeeping with React. Through this small project, I have a deep understanding of React and React Hooks, but there are still many shortcomings. I hope to improve myself through more learning.