“This is the first day of my participation in the More Text Challenge. For details, see more text Challenge.”

preface

When LOOKING for a job, I found that some companies use React as their technology stack, and the salary is good. In order to increase the bargaining chip for survival, I still need to learn React and add a survival skill. Since I have been developing projects with Vue2.0 for four years, I use the Vue2.0 development project ideas to learn React.

Front-end projects are made up of individual pages. For Vue, a page is made up of multiple components, and the page itself is a routing component. The same is true for React. Vue will provide a series of technical support to complete the development of a component. Starting from this series of technical support, you can find corresponding technical support in React to get started with React, such as how to develop component UI in React, how to use components in React, how to define component data in React, etc.

This column will take you through Vue2.0 to React17 along those lines.

1. Scaffolding

First of all, choose a scaffold to build a React project, there are many scaffolds in React, why choose UmiJS scaffold? No reason, this scaffold is similar to Vue Cli, at least the routing configuration is similar to Vue Router.

Build a React project using UmiJS. The steps are simple:

  • Create an empty directory somewhere, open the command line tool, and execute the commandmkdir myapp && cd myapp;
  • Execute the commandnpm create @umijs/umi-appCreate a React project;
  • Execute the commandnpm installInstall dependencies;
  • After the dependency is successfully installed, run the following commandnpm run startLaunch the project and open it in a browserhttp://localhost:8000Access items.

You can see the React project myApp directory structure as follows:

. ├ ─ ─ package. Json ├ ─ ─ the umirc. Ts ├ ─ ─ the env ├ ─ ─ dist ├ ─ ─ the mock ├ ─ ─ public └ ─ ─ the SRC ├ ─ ─ the umi ├ ─ ─ layouts/index. The TSX ├ ─ ─ pages ├── index. Less ├── indexCopy the code

Open the.umirc.ts file, which has the following contents

import { defineConfig } from 'umi';

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    { path: '/', component: '@/pages/index' },
  ],
  fastRefresh: {},
});
Copy the code

The route of the Vue Router is configured in the Routes option. The configuration is similar to that of the Vue Router.

Next, write a demo in SRC /pages/index.tsx to learn React.

How to develop UI for components in React

What is developed in Vue and React is a component, and the page is also a routing component. In Vue, components are defined in.vue files. In React, components are defined in.js files. When developing React in TypeScript, components are defined in.tsx files.

So how do you develop the UI part of a component, for example, a HelloWorld component that displays HelloWorld on a browser page, a component UI that has an HTML part and a CSS part.

2.1 HTML part

For the HTML part of the component, Vue recommends using the Template template and React recommends using the JSX syntax.

Create a helloWorld.js file in the SRC/Pages folder of your project and develop the HelloWorld component in it.

In addition, React components can be defined in two ways, one in functional form and one in ES6 class form.

Functional form, called function component:

export default function HelloWorld() {
  return (
    <div>hello world</div>
  );
}
Copy the code

ES6’s class form, called a class component:

import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); } render() { return ( <div>hello world</div> ); }}Copy the code

Note here that the first letter of the function name should be capitalized. Use JSX syntax to develop the UI part of the component after the return in the function.

Return (); otherwise, return must be preceded by at least one <, as shown below:

export default function HelloWorld() {
  return <
    div>hello world</div>
}
Copy the code

It looks weird, so it’s better to add ().

2.2 Binding Class and Style

The most important thing about the CSS part of a component is binding Class and Style to add Style to the HTML of the component.

Let’s see how Class and Style are fixed in React.

export default function HelloWorld() {
  return (
    <div 
      className="title head"
      style={{color:'red',fontSize:'16px'}}
    >
      hello world
    </div>
  );
}
Copy the code

When you React, you use className to bind Class and style to bind style. The value that style takes is an object, which is passed in {} brackets, and whose property names can only be named in camelCase.

Let’s see how Class and Style are variables bound in React.

  • Class component:
import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { styleData: { color: 'red', 'fontSize': "16px" }, isHead: true, className: 'title' }; } render() { return ( <div className={`${this.state.className} ${this.state.isHead ? 'head' : ''}`} style={this.state.styleData} > hello world </div> ); }}Copy the code
  • How to write a function component
import { useState } from 'react';
export default function HelloWorld() {
  const [styleData] = useState({ color: 'red', 'fontSize': "16px" });
  const [isHead] = useState(true);
  const [className] = useState('title');
  return (
    <div
      className={`${className} ${isHead ? 'head' : ''}`}
      style={styleData}
    >
      hello world
    </div>
  );
}
Copy the code

In React, {} is used to assign variables to attributes, and className accepts only strings, not arrays or objects. You can use ES6’s template string function to concatenate variables to generate strings.

Use the React Hook useState to define some variables in the function component. The functions of useState will be described later.

How to use components in React

The HelloWorld component is written. How to use it? To review how components are used in Vue, they must be registered before they can be used. They can be registered as global or local components.

There is no concept of registering a component in React, because a component is a function, only the concept of component is introduced, and there is no concept of global component. You must import and name a component with import before using it.

import HelloWorld from './HelloWorld.js'

export default function Index(){
  return (
    <HelloWorld/>
  )
}
Copy the code

Component names in React must start with an uppercase letter because React treats components that start with a lowercase letter as native DOM tags.

How to define component data in React

From the experience of developing Vue components, a component’s data can be divided into internal data and parameter data. The same is true for React, where internal data is called state and parameter data is called props.

4.1 Defining internal Data States

The React process, which shows how to bind classes and styles in variable form, already defines internal data such as styleData, isHead, and className.

  • In the class component is inthis.stateThis object defines the data:
this.state = {
  styleData: { color: 'red', 'fontSize': "16px" },
  isHead: true,
  className: 'title'
};
Copy the code
  • Used in the writing of function componentsuseStateThe React Hook defines the data:
const [styleData] = useState({ color: 'red', 'fontSize': "16px" });
const [isHead] = useState(true);
const [className] = useState('title');
Copy the code

It is recommended to develop the React component in functional form. React Hooks can be used to avoid learning ES6’s class syntax and annoying this pointing problems, so as to reduce the difficulty of getting started.

You can see the React Hook here.

4.2 Defining parameter data Props

Props to receive data passed externally to the component. For example, define the title parameter data in the HelloWorld component.

  • Class component:
import React from 'react';
class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>{this.props.title}</div>
    );
  }
}
HelloWorld.defaultProps = {
  title: 'hello world'
};
export default HelloWorld;
Copy the code

Constructor (functions) : functions (functions) functions (functions) functions (functions) functions (functions) functions (functions) functions (functions) functions (functions) functions (functions) functions We can use this.props. Title to read the value of the title parameter. We can also use defaultProps to define the default value of the title parameter.

  • How to write a function component
import { useState } from 'react';
export default function HelloWorld(props) {
  const {title = 'hello world'} = props;
  return (
    <div>{title}</div>
  );
}
Copy the code

The function component receives props as a collection of the component parameter data. ES6 uses the destruct assignment function to get the component parameter data and set default values for the parameter data.

Vue’s template uses {{}} (double curly braces), whereas React uses {} (single curly braces).

The Props parameter is used to receive data passed to the component externally. How do you pass data to the component in React?

How to pass data to components in React

Vue specifies how dynamic data and static data of strings, numbers, booleans, arrays, and object types are passed to components.

  • Transfer dynamic data
import { useState } from 'react';
import HelloWorld from './HelloWorld.js';
export default function Index(){
  const [styleData] = useState({ color: 'red', 'fontSize': "16px" });
  const [isHead] = useState(true);
  const [className] = useState('title');
  return (
    <HelloWorld 
       styleData={styleData} 
       isHead={isHead} 
       className={className}
    />
  )
}
Copy the code
  • Pass static data of type string
<HelloWorld title="hello vue"></HelloWorld>
Copy the code
  • Pass numeric static data
<HelloWorld num={1}></HelloWorld>
Copy the code
  • Pass static data of type Boolean
<HelloWorld isHead={false}></HelloWorld>
Copy the code
  • Pass static data of type array
<HelloWorld className={['title','head']}></HelloWorld>
Copy the code
  • Pass static data for the object type
<HelloWorld styleData={{color:'red','fontSize':"16px"}}></HelloWorld>
Copy the code

In React, instead of passing string static data, we pass the data to the component by wrapping it with {} and assigning it to the attribute on the component label.

How to listen for DOM events in React

6.1 Listening for DOM events of DOM elements

  • Class component:
import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); // In order to use 'this' in the callback, this binding is essential this.handleclick = this.handleclick.bind (this); } handleClick() {console.log(' click event '); } render() { return ( <div onClick={this.handleClick}>hello world</div> ); }}Copy the code
  • How to write a function component
Export default function HelloWorld() {const handleClick = ()=>{console.log(' click event '); } return ( <div onClick={handleClick}>hello world</div> ); }Copy the code

React uses onClick to listen for the click event. It uses {} to wrap the function that executes when the click event is triggered, and assigns a value to onClick.

Where onClick is a composite event, the @ in Vue is followed by a DOM native event, and the ON in React is followed by an event that is not native to the DOM. For example, Vue listens for the double-click event @dblclick, while React listens for the double-click event onDoubleClick.

The synthetic events in React can be seen here.

6.2 Listening for DOM events of the React component

Native modifiers are used in Vue to listen for DOM events on components, whereas in React to listen for DOM events on components.

For example, to listen for the click event on a component, pass the function to be executed when click is fired as Props. To listen for the event on the root element of the component, invoke the Props when click is fired. The specific implementation is as follows:

  • Class component:
import React from 'react'; export default class HelloWorld extends React.Component { constructor(props:any) { super(props); console.log(this.props) this.handleClick = this.handleClick.bind(this); } handleClick() { this.props.onClick(); } render() { return ( <div onClick={this.handleClick}>hello world</div> ); }}Copy the code
import React from 'react'; import HelloWorld from './HelloWorld'; Export default class Grandfather extends React.Component {handleClick() {console.log(' click event '); } render() { return ( <HelloWorld onClick={() => { this.handleClick() }}> </HelloWorld> ) } }Copy the code
  • How to write a function component
export default function HelloWorld(props) {
  const { onClick } = props
  const handleClick = () => {
    onClick();
  }
  return (
    <div onClick={handleClick}>hello world</div>
  );
}
Copy the code
import HelloWorld from './HelloWorld'; Export default function Index(){const handleClick = ()=>{console.log(' click event '); } return ( <HelloWorld onClick={() => { handleClick() }}> </HelloWorld> ) }Copy the code

How do components in React change data

As described above, React component data is divided into internal data (state) and parameter data (props).

7.1 Changing the Internal Data State

For example, you want to change the internal data title defined in the component.

  • Class component:
import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { title: 'hello world', className: 'title' }; // In order to use 'this' in the callback, this binding is essential this.handleclick = this.handleclick.bind (this); } handleClick() { this.setState(state => ({ title: 'hello react', className: 'title active' })); } render() { return ( <div className={className} onClick={this.handleClick} >{title}</div> ); }}Copy the code

We can pass a function or an object in this.setstate (). We recommend passing a function (state,props) =>{}. The function can take the internal data state and the parameter data props. Each time you call this.setState, the state and Props are the latest. This is especially applicable when you call this.setState several times to change the same state. Finally, the function puts back an object whose contents are the state to be modified.

  • How to write a function component
import { useState } from 'react';
export default function HelloWorld() {
  const [title,setTitle] = useState('hello world');
  const [className,setClassName] = useState('title');
  const handleClick = () =>{
    setTitle('hello react');
    setClassName('title active')
  }
  return (
    <div 
      className={className} 
      onClick={handleClick}
    >
      {title}
    </div>
  );
}
Copy the code

When you define a state using useState(param), you can use the param parameter to set the default value of state. It returns an array with the first value of state, and the second value of the array is a function that changes the state. You can call this function to change state.

In addition, the data defined by useState is responsive. If the page uses the data, the page will be rerendered after the data changes.

7.2 Changing parameter data props

If you want to change the props, you can only change the props indirectly by changing the data passed to the child component in the parent component. How to make the parent component change the data passed to the child component in the child component? How to communicate with the parent component in React will be introduced.

How do the parent and child components communicate in React

As an example, if the HelloWorld component’s “HelloWorld” is displayed with the title of the props parameter, clicking the change title button in the component will change to “Hello React.”

  • Class component:
import React from 'react'; class HelloWorld extends React.Component { constructor(props) { super(props); this.handleChangeTitle=this.handleChangeTitle.bind(this); } handleChangeTitle(){ this.props.changeTitle('hello React'); } to render () {return (< div > {this. Props. The title} < button onClick = {this. HandleChangeTitle. Bind (this)} > change the title < / button > </div> ); } } HelloWorld.defaultProps = { title: 'hello world' }; export default HelloWorld;Copy the code
import HelloWorld from './HelloWorld.js'
import React from 'react'
class Index extends React.Component {
  constructor(props) {
    super(props);
    this.state={
      info:'hello world'
    };
    this.handleChangeTitle=this.handleChangeTitle.bind(this);
  }
  handleChangeTitle(data){
    this.setState(state =>{
      return {
        info:data
      }
    })
  }
  render() {
    return (
      <HelloWorld 
        title={this.state.info} 
        changeTitle={this.handleChangeTitle}>
      </HelloWorld>
    );
  }
}
export default Index;
Copy the code
  • How to write a function component
export default function HelloWorld(props: any) { const { title = 'hello world', changeTitle } = props; const handleChangeTitle = () => { changeTitle('hello React') } return ( <div> {title} <button OnClick ={handleChangeTitle}> }Copy the code
import { useState } from 'react'
import HelloWorld from './HelloWorld.js'

export default function Index(){
  const [info,setInfo] = useState('hello world');
  const handleChangeTitle = (data)=>{
    setInfo(data);
  }
  return (
    <HelloWorld title={info} changeTitle={handleChangeTitle}/>
  )
}
Copy the code

The parent defines an info data to pass to the child’s title parameter data, and also defines a callback function, handleChangeTitle, to change the info data, and passes the callback function to the child’s changeTitle parameter data.

In this way, the component’s changeTitle parameter data can be called as a function. Calling changeTitle is equivalent to calling the handleChangeTitle callback. You can pass in the data parameter the value to be changed. Then setInfo(data) is executed to change the INFO data, and then the title parameter data is passed to the child component, which indirectly changes the title parameter data and realizes how the component in React changes the parameter data.

You can also call setInfo in the parent component to change the info of the title parameter data passed to the child component.

How to listen for component data changes in React

In Vue, you can simply use watch to listen for data changes, while in React, it is more complicated, and the implementation method is different depending on the type of sub-components.

  • Class component:

(componentDidUpdate) {componentDidUpdate = props/state; The first argument, prevProps, is the props before the change, and the second, prevState, is the state before the change.

componentDidUpdate(prevProps, prevState){ if(prevProps.title ! == this.props. Title){console.log('props. Title changed '); } if(prevState.info ! == this.state.info){console.log(' info in state changed '); }}Copy the code
  • The way a function component is written

In the function component, the React Hook useEffect can be used to listen for data changes, but the old data before changes cannot be obtained like Vue’s Watch. Therefore, a custom Hook should be defined to realize the function similar to Vue’s Watch. A custom Hook is a function whose name begins with “use”. Other hooks can be called inside the function. Therefore, the custom Hook is called useWatch.

How to obtain the old data before the change, you can use a container to store the old data when the useWatch is triggered for the first data change, and the old data before the change can be obtained by reading the value in the container when the useWatch is triggered next time. The container can be created using the useRef Hook.

UseRef returns a mutable ref object whose.current property is initialized to the passed parameter (initialValue). The returned REF object remains the same throughout the lifetime of the component.

import {useEffect,useRef} from 'react';
export function useWatch(value,callback){
  const oldValue = useRef();
  useEffect(() =>{
    callback(value,oldValue.current);
    oldValue.current=value;
  },[value])
}
Copy the code

However, useEffect is called once after the first rendering of the component, causing the callback to be executed once. In addition, Vue’s watch is configured with immediate to control the execution of the callback immediately after the first rendering of the component. By default, the callback function is not executed after the first rendering of the component, and then a useWatch is defined in hook.js.

First implement that the component render does not execute the callback function.

import {useEffect,useRef} from 'react'; export function useWatch(value,callback){ const oldValue = useRef(); const isInit = useRef(false); useEffect(() =>{ if(! isInit.current){ isInit.current = true; }else{ callback(value,oldValue.current); } oldValue.current=value; },[value]) }Copy the code

Add the immediate configuration to control whether the callback function is executed immediately after the component is first rendered.

import {useEffect,useRef} from 'react'; export function useWatch(value,callback,config={immediate: false}){ const oldValue = useRef(); const isInit = useRef(false); useEffect(() =>{ if(! isInit.current){ isInit.current = true; if(config.immediate){ callback(value,oldValue.current); } }else{ callback(value,oldValue.current); } oldValue.current=value; },[value]) }Copy the code

In addition, Vue’s watch returns an unwatch function that stops listening to the data.

import { useEffect, useRef } from 'react'; export function useWatch(value, callback, config = { immediate: false }) { const oldValue = useRef(); const isInit = useRef(false); const isWatch = useRef(true); useEffect(() => { if (isWatch.current) { if (! isInit.current) { isInit.current = true; if (config.immediate) { callback(value, oldValue.current); } } else { callback(value, oldValue.current); } oldValue.current = value; } }, [value]) const unwatch = () => { isWatch.current = false; }; return unwatch; }Copy the code

Use the useWatch Hook.

export {useState} from 'react';
export {useWatch} from './hook.js';
export default function HelloWorld() {
  const [title,setTitle] = useState('hello world')
  useWatch(title, (value, oldValue) => {
    console.log(value);
    console.log(oldValue)
  })
  const handleChangeTitle = () => {
    setTitle('hello React')
  }
  return (
    <div onClick={handleChangeTitle}>{title}</div>
  );
}
Copy the code

How can a parent component call a child component’s method in React

$refs[ID] = this.$refs[ID] = this.$refs[ID] = this. In React, it is more complex, and the implementation method is different depending on the type of subcomponent.

  • Class subcomponent
import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); this.state = { title:'hello World' }; } handleChangeTitle(){ this.setState({ title:'hello React' }); } render() { return ( <div> {this.state.title} </div> ); }}Copy the code
import React from 'react'; import HelloWorld from './HelloWorld.js'; class Index extends React.Component { constructor(props) { super(props) this.myCom = React.createRef(); this.changeTitle = this.changeTitle.bind(this); } changeTitle() { this.myCom.current.handleChangeTitle(); } render() {return (<div> <HelloWorld ref={this.mycom} /> <button onClick={this.changetitle}> } export default Index;Copy the code
  • How to write a function component

UseRef () cannot be used on a function component. Use the useImperativeHandle to define the instance value to be exposed to the parent component, and pass the function into the forwardRef before exporting it.

import { useState, forwardRef, useImperativeHandle } from 'react';

const HelloWorld = (props, ref) => {
  const [title, setTitle] = useState('hello World');
  useImperativeHandle(ref, () => ({
    handleChangeTitle: () => {
      setTitle('hello React')
    },
  }));
  return (
    <div>{title}</div>
  );
}
export default forwardRef(HelloWorld)
Copy the code
import { useRef } from 'react' import HelloWorld from './HelloWorld.js' export default function Index() { const myCom = useRef(); const changeTitle = () => { myCom.current.handleChangeTitle(); } return (<div> <HelloWorld ref={myCom} /> <button onClick={changeTitle}>)}Copy the code

How to implement component slots in React

11.1 Common Slots

There are no sockets in React, but you can use props. Children to do so.

Each component can get props. Children. It contains the content between the start and end tags of the component.

For example, create a HelloWorld component that displays “HelloWorld.” You can also use a slot that passes “HelloWorld” from the parent component using props. Children.

Class component:

import React from 'react'; export default class HelloWorld extends React.Component { constructor(props) { super(props); } render() { return ( <div> {this.props.children} </div> ); }}Copy the code
import React from 'react';
import HelloWorld from './HelloWorld.js';
class Index extends React.Component{
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <HelloWorld>hello World</HelloWorld>
    )
  }
}
export default Index;
Copy the code

How to write a function component

export default function HelloWorld(props){
  const {children}=props;
  return (
    <div>
      {children}
    </div>
  );
}
Copy the code
import HelloWorld from './HelloWorld.js';
export default function Index(){
  return (
    <HelloWorld>hello World</HelloWorld>
  )
}
Copy the code

11.2 Named Slot

You can pass a function to a child component using props. If the function returns a React element, the React element is written in JSX syntax, thereby indirectly implementing the named slot function.

For example, develop a HelloWorld component that displays “HelloWorld” in the form of a slot for the tool name.

Class component:

import React from 'react';
class HelloWorld extends React.Component{
  constructor(props){
    super(props)
    this.elementSlot = "";
    if (this.props.element) {
      this.elementSlot = this.props.element();
    }
  }
  render(){
    return (
      <div>
        {this.elementSlot}
      </div>
    );
  }
}
export default HelloWorld;
Copy the code
import React from 'react';
import HelloWorld from './HelloWorld.js';
class Index extends React.Component{
  constructor(props) {
    super(props);
  }
  info(){
    return(
      <span>hello World</span>
    )
  }
  render() {
    return (
      <HelloWorld element={this.info}></HelloWorld>
    )
  }
}
export default Index;
Copy the code

How to write a function component

export default function HelloWorld(props) {
  const { children, element } = props;
  let elementSlot = "";
  if (element) {
    elementSlot = element();
  }
  return (
    <div>
      {elementSlot}
    </div>
  );
}
Copy the code
import HelloWorld from './HelloWorld.js';

export default function Index(){
  const info = () =>{
    return (
      <span>hello World</span>
    )
  }
  return (
    <HelloWorld element={info}></HelloWorld>
  )
}
Copy the code

11.3 Scope slots

The purpose of a Vue’s scoped slot is to write the slot contents to the parent component using data from the child component.

We defined a function in the parent component that returns a React element. We passed the function to the child component via props. We executed the function in the child component and added the result to the React element of the child component.

If this function is executed in the child component and the child data is passed as an argument, then the parent component can use this function to receive the child data and write the React element (the contents of the slot). This implements the scoped slot.

For example, develop a HelloWorld component that displays “write a named slot HelloWorld with data from a child component” in the form of a scoped slot.

Class component:

import React from 'react'; Class HelloWorld extends React.Com {constructor(props){super(props) this.state = {info:' write a named socket hello with data from a child component World' } this.elementSlot = ""; if (this.props.element) { this.elementSlot = this.props.element(this.state.info); } } render(){ return ( <div> {this.elementSlot} </div> ); } } export default HelloWorld;Copy the code
import React from 'react';
import HelloWorld from './HelloWorld.js';
class Index extends React.Component{
  constructor(props) {
    super(props);
  }
  info(data){
    return(
      <span>{data}</span>
    )
  }
  render() {
    return (
      <HelloWorld element={this.info}>
      </HelloWorld>
    )
  }
}
export default Index;
Copy the code

The way a function component is written

import { useState } from "react"; export default function HelloWorld(props) { const { children, element } = props; Const [info] = useState(' write hello World with subcomponent data ') let elementSlot = ""; if (element) { elementSlot = element(info); } return ( <div> {elementSlot} {children} </div> ); }Copy the code
import HelloWorld from './HelloWorld.js';

export default function Index(){
  const info = (data) =>{
    return (
      <span>{data}</span>
    )
  }
  return (
    <HelloWorld element={info}></HelloWorld>
  )
}
Copy the code

summary

The techniques used by Vue to develop a component are described above, and corresponding implementation solutions are found in React. After reading React, you should be able to develop some simple pages with React, divide the pages into components, and conduct data interaction between components. The next article will show how the directives that are commonly used in Vue when developing components are implemented in React.