React Learning (5) - Advanced Applications: Pro Type Check and True Dom Operation

Keywords: React Javascript Attribute TypeScript

Use PropTypes for type checking

As applications grow, many bug s can be found through type checking.For some applications, you can use JavaScript extension tools, such as Flow or TypeScript To check the whole project.In addition to introducing external tools, React also provides the ability to check parameter types by specifying only one propTypes for each property:

//Define Components
class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

//Specify Type Check
Greeting.propTypes = {
  name: React.PropTypes.string
};

React.PropTypes will set up a series of validators that are used to ensure that the parameters (props) accepted by the component are of the specified type.For example, in the example above, when an incorrect type is received by a component, there will be a warning for output through console.propsTypes are only used in development mode.

React.PropTypes

The following are examples of various validators:

MyComponent.propTypes = {
  // Indicates the specific type of each incoming parameter, passing only those JavaScript built-in types
  optionalArray: React.PropTypes.array,
  optionalBool: React.PropTypes.bool,
  optionalFunc: React.PropTypes.func,
  optionalNumber: React.PropTypes.number,
  optionalObject: React.PropTypes.object,
  optionalString: React.PropTypes.string,
  optionalSymbol: React.PropTypes.symbol,

  // number, string, element, or a list are allowed
  optionalNode: React.PropTypes.node,

  // Receive a React component
  optionalElement: React.PropTypes.element,

  // Declares that this parameter receives only an instance of an object (class), suitable for passing an object as a configuration parameter
  optionalMessage: React.PropTypes.instanceOf(Message),

  // Specified parameters are limited to multiple objects
  optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),

  // Specifying parameters allows multiple types
  optionalUnion: React.PropTypes.oneOfType([
    React.PropTypes.string,
    React.PropTypes.number,
    React.PropTypes.instanceOf(Message)
  ]),

  // List of specified types
  optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),

  // Specifies that a type is passed, that an object is not the data itself
  optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),

  // Specifies the structure of the transfer parameter, applicable to the structure of the object when it is passed
  optionalObjectWithShape: React.PropTypes.shape({
    color: React.PropTypes.string,
    fontSize: React.PropTypes.number
  }),

  // Indicates that this parameter is a must-pass parameter that must pass in data when using this component
  requiredFunc: React.PropTypes.func.isRequired,

  // Allow any type of data.
  requiredAny: React.PropTypes.any.isRequired,

  // Specify a custom inspector that returns an Error object to indicate an error when the check fails.
  // Errors only need to be returned, remember that throw or console.warn output cannot be used
  // Not applicable to oneOfType type.
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // A custom checker for detecting an array pass, suitable for arrayOf and objectOf types.
  // Error needs to be returned when a check error occurs
  customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

Limit receiving at least one child element

You can use React.PropTypes.element to indicate that a component must receive a child element:

class MyComponent extends React.Component {
  render() {
    // This must be exactly one element or it will warn.
    const children = this.props.children;
    return (
      <div>
        {children}
      </div>
    );
  }
}

MyComponent.propTypes = {
  children: React.PropTypes.element.isRequired
};

Set props default

You can also use defaultProps to specify default values:

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

// Specify the default value for props.name:
Greeting.defaultProps = {
  name: 'Stranger'
};

ReactDOM.render(
  <Greeting />,
  document.getElementById('example')
);

Refs and Real Dom

In a typical React data stream, the props parameter passes through the only interface.When you need to modify the parameters, you must modify the props value and render it again.However, there are many scenarios where subcomponents need to be modified outside of a one-way data stream, and React provides a "Refs" feature to modify the real Dom elements directly.

When do I need to use Refs

Refs features are recommended when you encounter the following situations:

  • When you need to manage real Dom events such as focus, document selection, or media playback.
  • Triggers an animation that needs to be executed immediately.
  • When third-party libraries are introduced.

Avoid using Refs for any declarative work, such as replacing Dialog's open() and close() interfaces with a props.isOpen parameter.

Add Ref to Dom element

React supports using ref on any component.The ref property provides a callback method that is called when a component is rendered or removed.

When the ref attribute is used on an HTML element, the callback method of ref takes an instance of Dom.For example, the following example takes a Dom instance of the input tag and saves it to this.textInput variable, which points all the way to the Dom node.

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.focus = this.focus.bind(this);
  }

  // Define a focus method
  focus() {
    this.textInput.focus();
  }

  render() {
    return (
      <div>
        <input
          type="text"
          ref={(input) => { this.textInput = input; }} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focus}
        />
      </div>
    );
  }
}

When the Dom element is rendered, React calls back the method specified by ref and passes an instance of the current Dom as a parameter. When the Dom is removed, the method pointed to by ref is also called, and the parameter passed in is null.

Setting the class's properties using the ref callback method is a common way to get a real Dom object. The example above gives a way to write it, as long as the syntax is correct, in a variety of ways, such as shorter: ref={input => this.textInput = input}.

Add a Ref attribute to the class component

When ref is used on a custom component declared by the class keyword, the callback method pointed to by ref is called back after the component has finished rendering, and the passed parameter is an instance of the component.For example, the following example simulates a focus event immediately after the CustomTextInput component has finished rendering:

class AutoFocusTextInput extends React.Component {
  componentDidMount() {//Callback when finished rendering
    this.textInput.focus();//Focus on the current component
  }

  render() {
    // CustomTextInput has been declared in the previous section of code
    return (
      <CustomTextInput
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

The CustomTextInput component must be defined in class to take effect.

Set Refs for Function declared components

ref can no longer be used directly by components defined by a function because it was not instantiated at the time of declaration:

function MyFunctionalComponent() {
  return <input />;
}

class Parent extends React.Component {
  render() {
    // Error, ref here will have no effect.
    return (
      <MyFunctionalComponent
        ref={(input) => { this.textInput = input; }} />
    );
  }
}

The most reasonable way to do this is to convert functionally defined components into classes, which is why we need to use a state to control the state.However, in a function component, if another class component is referenced internally, the Refs feature can also be used:

function CustomTextInput(props) {
  // Declare textInput here, and a new local variable will be generated each time the rendering is re-rendered
  let textInput = null;

  // A callback method is generated each time the rendering is re-rendered
  function handleClick() {
    textInput.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={(input) => { textInput = input; }} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );  
}

Do not overuse Refs features

Perhaps after understanding the mechanisms behind Refs, some developers prefer to use Refs as an "action occurs" feature in their code to implement functionality.But before using it, it's best to spend some time thinking about why states need to be controlled by different component hierarchies. Usually, the states between components are best controlled by their common ancestors. Read more "State Sharing" Relevant instructions.

*Use Warning

If ref's callback method is defined as an inline method, it will be called twice before updating, passing a null value on the first call and giving the real Dom object the second time.This is because a new method instance is created each time rendering occurs, so React must clear the existing ref and create one.This can be avoided by defining the ref callback method as a binding method for a class, but be aware that in most cases this will not cause any problems.

Posted by rhodrykorb on Fri, 12 Jul 2019 09:30:36 -0700