彻底理解react中的props

每天对自己多问几个为什么,总是有着想象不到的收获。 一个菜鸟小白的成长之路(copyer)


​在react中 state 和 props是两个非常重要的属性,并且常用的属性。简单来说:都是用来保存数据状态的。

需注意的是state,props的每一次改动都会使页面重新渲染一次。


state的解释

state 意为 状态,组件内部的状态。

state是一个可变的属性,控制组件内部的状态

state 一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等


props的解释

PropsProperties的简写。

Props用于组件之间的通信,一种数据传递的方式。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递。

props是只读属性。想要修改props,只能从父组件修改,然后传递给子组件。


props的形成

都知道, react 中的 jsx 会通过 babel转化成 createElement函数。所以,这里主要就是要知道createElement这个函数。话不多说,先看实例,然后看createElement的源码。

jsx转化成createElement的实例(babel官网的转化)

在这里插入图片描述

根据上面的转化和图解:

可以看,createElement 接受多个参数,

第一个参数为组件的名称(大写字母就是组件的名称,小写字母开头,就是原生的html标签),

第二个参数为props,如果没有props,就是为 null

第三个参数及跟多的参数(可选参数)

  • 如果没有子元素,就是undefined
  • 如果有子元素,那么子元素的createElement的函数就作为参数,如果有多个子元素,就依次排列下去(跟vue3的h函数有点区别,h函数的三个参数是数组,存放子元素的h函数)

看完了上面的分析,是不是对React.createElement函数有点认识了啊,那么接下来看看源码是怎么实现的吧。(这个函数不复杂,但是我还是省略一些代码,便于理解)

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,
  );
}

看望上面的代码,就可以很清楚的了解props的组成。

  • let props = {} props是一个对象
  • propName in config 循环createElement的第二个参数config,第二个参数,就是组件中写的props
  • props.children 就是组件中是否存在子元素。如果没有子元素,值就是undefined;如果是有一个子元素,值就是ReactElement元素;如果有多个,值就是一个数组,数组中存着ReactElement元素

props.children

上面的源码解析中已经中分析了,可以继续看上面的分析


defaultProps

在定义中组件的时候,我们可以给组件的props设置默认值,可以有效的防止没有传递对应的props,是程序报错

具体用法:

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

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

在上面使用Test组件的时候,如果传递了title,那么Test中,title就是kobe。如果没有传递title,那么组件中的title就是james


源码分析

createElement中的一个参数,就是组件的名称(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:就是组件取个名字,便于区分。

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

用途:

  • 在react的设置设计模式中的组合模式,就需要通过判断传递的子组件来进行渲染。
  • 调试工具的调试的名称显示

PropTypes

对组件的props的类型限定。如果JavaScript是个弱语言,会自动进行隐式转化,容易造成react的程序报错。所以,我们就需要对类型进行限定。

所以,prop-types 就很好对组件的类名进行限定。

安装:

npm install --save prop-types

官网地址:

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

示例:

import PropTypes from 'prop-types';

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

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

类型限定:

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.'
      );
    }
  })
};

哈哈哈,上面的有点多,不用记太多,记住常用的就行了。如果有特殊的需求,就再去查官网吧。


TypeScript对props的限定

在上面,就是对props的类型限定和默认值的设置,在ts中就能完美的解决。

类组件

React.Component<P, S>

P: 就是对props的类型进行限定。

S:就是对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
        };
    }

函数组件

React.FC<IProps>

IProps: 就是对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;
}

props的类型 :PropsWithChildren<P>

这里PropsWithChildren中内部实现了props新增children属性。


总结

​ 目前对props的所有认识,就在这里,以后遇到新的知识,然后在进行补充。如果上面的有错误,可以提出来哟。


附加信息: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,
  );
}
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Reactprops和state是两种不同的数据类型。 propsproperties)是父组件传给子组件的数据,子组件只能读取这些数据,不能修改。 state(状态)是组件自身维护的数据,组件可以读取并修改这些数据。 简单来说,props是用来给组件传递参数,state是用来维护组件的状态。 ### 回答2: React props 和 state 是两个非常重要的概念,它们有着不同的特点和作用props 和 state 的主要区别在于它们的来源、可变性和使用方式。 1. 来源 props 是组件之间传递数据的一种机制,是由父组件传递给子组件的。父组件可以通过 props 向子组件传递任意类型的数据,包括数字、字符串、对象、数组、甚至函数等。子组件不能通过 props 修改传递过来的数据,props 只读。 state 是组件内部自身管理的一种数据,每个组件都有自己的 state 状态。组件可以通过调用 setState 方法来修改自己的 state 数据。 2. 可变性 props 是只读的,是不可变的。它们被设计成只能由父组件传递给子组件,子组件不能修改 props。如果需要修改 props 的值,只能在父组件进行修改。 state 是可变的,它们被设计成可以由组件自身进行修改,组件内部调用 setState 方法来改变自己的状态。当修改 state 时,React 会自动重新渲染组件。 3. 使用方式 props 通常用于传递数据和回调函数,使不同组件之间可以进行通信和交互。父组件向子组件传递 props,子组件从 props 获取数据进行渲染,同时通过回调函数进行数据的传递和事件的触发。 state 用于存储组件的状态和数据,可以更新组件的显示和行为。当组件的状态改变时,React 会自动重新渲染组件,并根据新的状态计算出新的渲染结果。 总之,props 和 state 都是 React 组件很重要的数据管理机制。它们各自的特点和使用方式也是不同的。props 主要用于组件之间的通信、数据传递和事件处理,state 则主要用于控制组件自身的状态和行为。需要根据实际情况和需求,选择合适的数据管理方式。 ### 回答3: React是一款非常强大的JavaScript库,被广泛用于基于组件的Web开发。在React开发props和state是两个非常重要的概念,也是非常容易混淆的概念。 Props是组件之间传递数据的一种方式,通常被用于从父组件向子组件传递数据。Props是一个JavaScript对象,可以包含任何类型的数据,例如字符串、数组、对象等等。在组件内部,我们可以通过this.props来访问这些数据,并使用它们来渲染组件。 而State是React组件的另一个重要概念,表示组件内部的状态数据。在组件内部定义一个state对象,该对象内部包含了组件的状态数据。State是私有的,只能由组件本身进行修改,其他组件无法访问。当state数据发生变化时,React会自动重新渲染组件。 那么,props和state有什么区别呢? 首先,props是外部数据,通常由父组件向子组件传递,而state是组件内部的状态数据,只能由组件内部进行修改。 其次,props是只读的,一旦被传递给子组件,就无法再被修改,而state是可以被修改的,并且当state发生变化时,React会自动重新渲染组件。 最后,props是使组件“可配置”的一种方式,允许我们根据不同的数据来渲染不同的组件。而state是使组件“可动态”的一种方式,允许我们根据组件内部的状态数据来动态更新组件。 在开发React组件时,我们通常会用props来传递外部数据,而state则用于存储组件内部的状态数据。这两种概念的合理应用,可以使组件更加灵活、易维护,并且更好地符合React的设计理念。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值