Thoroughly understand props in react

Ask yourself a few more whys every day, and you will always get unexpected results. The growth path of a rookie Xiaobai (copyer)


​State and props are two very important and commonly used attributes in react. To put it simply: it is used to save the data state.

It should be noted that every change of state and props will cause the page to be re-rendered.


state explanation

​It meansstate state, the state inside the component.

​Isstate a mutable property that controls the internal state of the component

​Generallystate used for state maintenance inside the component, updating data and state inside the component, updating props of sub-components, etc.


Explanation of props

Props​yes Propertiesshorthand.

​Used forProps communication between components, a way of data transfer. Since React's data flow is top-down, it is passed from the parent component to the child component.

​isprops a read-only property. If you want to modify props, you can only modify it from the parent component and then pass it to the child component.


The formation of props

We all know that in react jsxwill be babeltransformed into createElementa function. Therefore, the main thing here is to know the function createElement. Not much to say, first look at the example, and then look at the source code of createElement.

Jsx is converted into an instance of createElement (transformation of babel official website)

insert image description here

According to the conversion and diagram above:

As you can see, createElementit accepts multiple parameters,

The first parameter is the name of the component ( 大写字母that is, the name of the component, 小写字母at the beginning, is the original html tag),

The second parameter is props, if there is no props, it isnull

The third parameter and more parameters (optional parameters) :

  • If there are no child elements, it isundefined
  • If there is a sub-element, 子元素的createElement的函数it will be used as a parameter. If there are multiple sub-elements, it will be arranged in order (it is vue3的h函数a bit different, the three parameters of the h function are arrays, which store the h function of the sub-elements)

After reading the above analysis, do you have a little understanding of the React.createElement function, so let's take a look at how the source code is implemented. (This function is not complicated, but I still omit some codes for easy understanding)

const RESERVED_PROPS = {
    
        // 处理props, 排除特殊的关键字 key ref 等等
  key: true,
  ref: true,
  __self: true,
  __source: true,
};

export function createElement(type, config, children) {
    
    
  let propName;

  const props = {
    
    };   //  定义一个props的空对象
    
    
  //... 省略代码
    
    
  // 遍历 config 赋值给 props
  for (propName in config) {
    
      
        if (
            hasOwnProperty.call(config, propName) &&  // propsName在config中
            !RESERVED_PROPS.hasOwnProperty(propName)  // propsName不是关键词
        ) {
    
    
            props[propName] = config[propName];
        }
    }
  }

  // 获取第二个参数后面的参数个数 
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    
       // 如果长度为1,就是一个ReactElement
    props.children = children;
  } else if (childrenLength > 1) {
    
      // 如果长度为1, 就是一个数组,保存着多个ReactElement
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
    
    
      childArray[i] = arguments[i + 2];
    }
    // ...
    props.children = childArray;
  }

  return ReactElement( // 生成一个ReactElement的虚拟DOM
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

Looking at the above code, you can clearly understand the composition of props.

  • let props = {}props is an object
  • propName in configThe second parameter config of loop createElement, the second parameter is the props written in the component
  • props.childrenIt is whether there are child elements in the component. If there is no sub-element, the value is undefined; if there is a sub-element, the value is ReactElement元素; if there are multiple, the value is an array, which is stored in the array ReactElement元素.

props.children

The above source code analysis has been analyzed, you can continue to read the above analysis


defaultProps

When defining a component, we can set the props of the component 默认值, which can effectively prevent the corresponding props from being passed, yes 程序报错.

Specific usage:

// 定义组件
const Test = (props) => {
    
    
  const {
    
     title } = props
  return (
    <div>{
    
    title}</div>
  )
}
Test.defaultProps = {
    
    
  title: 'james'
}

//使用组件
const App = () => {
    
    
    return (
    	<div>
        	<Test title="kobe" />  
        </div>
    )
}

When using the Test component above, if the title is passed, then in the Test, the title is kobe. If no title is passed, the title in the component is james.


Source code analysis

A parameter in createElement is组件的名称(type)

// 源码部分处理默认值
if (type && type.defaultProps) {
    
    
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
    
        // 循环默认props,对象
      if (props[propName] === undefined) {
    
       // 如果在props中的值为undefined,使用默认值
        props[propName] = defaultProps[propName];
      }
    }
  }

displayName

displayName: It is the name of the component, which is easy to distinguish.

function Test() {
    
    
    
}
Test.displayName = 'OwnTest'   // OwnTest用于展示

use:

  • In the setting of react 设计模式中的组合模式, it is necessary to render by judging the passed sub-components.
  • The name of the debug tool's debug display

PropTypes

Type-qualifies the props of the component. If JavaScript is a weak language, it will automatically perform implicit conversion, which will easily cause the react program to report an error. Therefore, we need to limit the type.

Therefore, prop-typesit is good to qualify the class name of the component.

Install:

npm install --save prop-types

Official website address:

https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html#gatsby-focus-wrapper

Example:

import PropTypes from 'prop-types';

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

Greeting.propTypes = {
    
    
  name: PropTypes.string
};

Type qualification:

import PropTypes from 'prop-types';

MyComponent.propTypes = {
    
    
  // 你可以将属性声明为 JS 原生类型,默认情况下
  // 这些属性都是可选的。
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 任何可被渲染的元素(包括数字、字符串、元素或数组)
  // (或 Fragment) 也包含这些类型。
  optionalNode: PropTypes.node,

  // 一个 React 元素。
  optionalElement: PropTypes.element,

  // 一个 React 元素类型(即,MyComponent)。
  optionalElementType: PropTypes.elementType,

  // 你也可以声明 prop 为类的实例,这里使用
  // JS 的 instanceof 操作符。
  optionalMessage: PropTypes.instanceOf(Message),

  // 你可以让你的 prop 只能是特定的值,指定它为
  // 枚举类型。
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 一个对象可以是几种类型中的任意一个类型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 可以指定一个数组由某一类型的元素组成
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 可以指定一个对象由某一类型的值组成
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 可以指定一个对象由特定的类型值组成
  optionalObjectWithShape: PropTypes.shape({
    
    
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // An object with warnings on extra properties
  optionalObjectWithStrictShape: PropTypes.exact({
    
    
    name: PropTypes.string,
    quantity: PropTypes.number
  }),

  // 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
  // 这个 prop 没有被提供时,会打印警告信息。
  requiredFunc: PropTypes.func.isRequired,

  // 任意类型的必需数据
  requiredAny: PropTypes.any.isRequired,

  // 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
  // 请不要使用 `console.warn` 或抛出异常,因为这在 `oneOfType` 中不会起作用。
  customProp: function(props, propName, componentName) {
    
    
    if (!/matchme/.test(props[propName])) {
    
    
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
  // 它应该在验证失败时返回一个 Error 对象。
  // 验证器将验证数组或对象中的每个值。验证器的前两个参数
  // 第一个是数组或对象本身
  // 第二个是他们当前的键。
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    
    
    if (!/matchme/.test(propValue[key])) {
    
    
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

Hahaha, the above is a bit too much, so you don’t need to remember too much, just remember the commonly used ones. If you have special needs, check the official website again.


TypeScript's restrictions on props

In the above, it is the type limitation and default value setting of props, which can be perfectly solved in ts.

class component

React.Component<P, S>

P : It is to limit the type of props.

S : It is the type limit for state

// node_modules/@types/react/index.d.ts

class Component<P, S> {
    
    
        //设置静态属性
        static contextType?: Context<any>;
        context: any;
        //限定props的类型,为readonly类型,只读的
        constructor(props: Readonly<P>);
       
        constructor(props: P, context?: any);
        
        setState<K extends keyof S>(
            state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
            callback?: () => void
        ): void;

        forceUpdate(callback?: () => void): void;
        
        //render的类型
        render(): ReactNode;
        
        //对props的类型限定
        readonly props: Readonly<P> & Readonly<{
    
     children?: ReactNode }>;
        
        //state的类型限定
        state: Readonly<S>;
        
        refs: {
    
    
            [key: string]: ReactInstance
        };
    }

function component

React.FC<IProps>

IProps : is to limit the type of props

type FC<P = {
    
    }> = FunctionComponent<P>;

interface FunctionComponent<P = {
    
    }> {
    
    
    
    //对props进行限定, 返回值是一个ReactElement对象,为了生成虚拟DOM
    (props: PropsWithChildren<P>, context?: any): ReactElement | null;
    propTypes?: WeakValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

The type of props:PropsWithChildren<P>

Here PropsWithChildren internally implements the new children property of props.


Summarize

​ All the current understanding of props is here, and new knowledge will be encountered in the future, and then supplemented. If there is an error in the above, you can point it out.


Additional information: complete source code of createElement

/**
 * Create and return a new ReactElement of the given type.
 * See https://reactjs.org/docs/react-api.html#createelement
 */
export function createElement(type, config, children) {
    
    
  let propName;

  // Reserved names are extracted
  const props = {
    
    };

  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  if (config != null) {
    
    
    if (hasValidRef(config)) {
    
    
      ref = config.ref;

      if (__DEV__) {
    
    
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }
    if (hasValidKey(config)) {
    
    
      key = '' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // Remaining properties are added to a new props object
    for (propName in config) {
    
    
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)  // reserved_props
      ) {
    
    
        props[propName] = config[propName];
      }
    }
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    
    
    props.children = children;
  } else if (childrenLength > 1) {
    
    
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
    
    
      childArray[i] = arguments[i + 2];
    }
    if (__DEV__) {
    
    
      if (Object.freeze) {
    
    
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }

  // Resolve default props
  if (type && type.defaultProps) {
    
    
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
    
    
      if (props[propName] === undefined) {
    
    
        props[propName] = defaultProps[propName];
      }
    }
  }
  if (__DEV__) {
    
    
    if (key || ref) {
    
    
      const displayName =
        typeof type === 'function'
          ? type.displayName || type.name || 'Unknown'
          : type;
      if (key) {
    
    
        defineKeyPropWarningGetter(props, displayName);
      }
      if (ref) {
    
    
        defineRefPropWarningGetter(props, displayName);
      }
    }
  }
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

Guess you like

Origin blog.csdn.net/James_xyf/article/details/120775014