# React基础

TIP

什么是React

  • React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理view这一层
  • 核心专注于视图,目的实现组件化开发

什么是组件化开发? 我们可以很直观的将一个复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式 再将这些独立组件组合完成一个复杂的页面。 这样既减少了逻辑复杂度,又实现了代码的重用

  • 可组合
  • 可重用
  • 可维护

# react 脚手架安装

安装

在全局下安装create-react-app

打开命令行工具输入以下代码

npm install create-react-app -g

默认会自动安装React,react-dom两部分

# 使用 react 脚手架创建项目

TIP

需要在哪个文件夹下创建react项目就在那个项目下打开命令行工具输入以下代码

create-react-app xxx  # xxx为项目名称:不能出现大写字母
cd xxx   # 转到项目目录下
yarn start 或 npm start # 启动项目即可

# reactreact-dom 模块

TIP

js文件的顶部导入reactreact-dom

  • react:核心模块(生命周期等钩子函数都是在这里定义的) react 中常用的方法:

    • createElement:在shouldComponentUpdate()中实现时,使用了 propsstate 的浅比较。
    • Children:我们可以用React.Children.map去遍历this.props.children获取到的结果
    • Component:继承需要用到的
    • PureComponent:纯函数
  • react-dom:提供与DOM相关的功能 react-dom中常用的方法:

    • render :是 React的最基本方法,用于将模板转为 HTML 语言,并插入指定的 DOM 节点
import React from 'react';//核心基础 模块(生命周期)
//用来把react元素(jsx元素)渲染成真实的DOM结构并添加到页面当中
import ReactDOM,{render} from 'react-dom';
//ReactDOM中就一个方法比较常用叫render
//可以把render从react-dom中解构出来

# react 中的 jsx 语法与 render 渲染方法

# JSX 语法

语法

react构建页面有自己的语法:JSX语法;

jsx是一种JShtml混合的语法,HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 jsx 的语法,它允许 HTMLJavaScript 的混写;jsx最后会通过babel进行转义

jsx中的html部分和原生html基本一样(也有不一样的比如class要用classNamelabelfor要用htmlFor),并且属性名要使用驼峰命名法; 用jsx语法写的内容叫做jsx元素(虚拟DOM元素); jsx元素具有不变性 :只在页面初次加载的时候渲染一次,以后只能通过更改状态和属性来重新渲染;

jsx表达式的用法

  • 1、可以放JS的执行结果:在react中想将js当作变量引入到jsx中需要使用{}
  • 2、在jsx中,相邻的两个jsx元素 渲染时需要外面包裹一层元素(只能有一个根元素)
function build(str) {
  return <h1>{str.name}</h1><h1>{str.name}</h1>;//在jsx语法中,这种写法是错误的
}
  • 3、如果多个元素需要在return后面换行,我们需要加一个()当作一个整体返回
  • 4、可以把JSX元素当作函数的返回值
  • 5、用<{}来区分是html还是js表达式:遇到 HTML 标签(以< 开头),就用 HTML 规则解析;遇到代码块(以{开头),就用 JavaScript 规则解析
  • 6、循环绑定需要给元素设置唯一的KEY属性(属性值是唯一的),目的是为了方便React做DOMdiff

jsx属性的用法

jsx中分为普通属性和特殊属性

  • 1、普通属性:和html中的一样
  • 2、特殊的属性如class,要使用classNameforlabel中的for)要使用htmlFor
  • 3、style要采用对象的方式
import React from 'react';
import ReactDOM,{render} from 'react-dom';
let str='<h1>纯标签</h1>';
let style={background:'red'};
render(<div>
  <li className="aa"></li>
  <li htmlFor="aa" style={style}></li>
  <li dangerouslySetInnerHTML={{__html:str}}></li>
</div>,window.root);
  • 4、dangerouslyInnerHTML插入html(基本上用不到)

# React.createElement

React.createElement用来创建一个react对象,具有一个type属性代表当前的节点类型,还有节点的属性props;

React.createElement("div",null,"姜,",React.createElement("span",null,"帅哥"))
//结果如下
{type:'div',props:{className:"red",children:['姜',{type:'span',props:{id:'handsome',children:'帅哥'}}]}}

# render

TIP

此方法是由react-dom模块中提供的方法 第一个参数为要渲染的jsx元素 第二个参数为要把渲染的DOM结构插入到哪个容器当中 第三个参数为渲染完成后需要执行的回调函数;

  • 用来将虚拟DOM元素(jsx元素)渲染成真实的DOM
ReactDOM.render(<div className='app'><span id='handsome'>帅哥</span></div>,window.root);

# react 中的虚拟 DOM

我们在react中创建的组件和用jsx语法写出的标签等,都是虚拟的DOM元素:

组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOMvirtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。

# 虚拟 DOM 渲染过程(虚拟 DOM —>真实 DOM

# 1、构建虚拟DOM元素(jsx元素):

TIP

  • 在js中用jsx语法写的jsx元素就是虚拟DOM元素;(render中的元素) render(<h1 className='red'>我是谁,我在哪</h1>,window.root)

# 2、调用render方法把虚拟DOM元素渲染成真实的DOM结构

TIP

  • 1)render方法会先用babeljsx元素转化为ES5语法 'h1', {className: 'red'}, '我是谁,我在哪'

  • 2)再用React.createElement方法根据上面的结果创建出一个react对象 React.createElement('h1', {className: 'red'}, '我是谁,我在哪')结果如下 {type: "h1", props: {className: "red", children: "我是谁,我在哪"}}

  • 3)此时render方法将react对象转化为真正的DOM结构,渲染到页面上 render(<h1 className='red'>我是谁,我在哪</h1>,window.root)

自己模拟React.createElement方法与render方法:(render不完善)

{type:'div',props:{className:"red",children:['姜',{type:'span',props:{id:'handsome',children:'帅哥'}}]}}
function createElement(type,props,...children) {
  if(children.length === 1) children = children[0];
  return {type,props:{...props,children:children}}
}
let myRender = (obj, container,callback) => {
  let {type, props} = obj;
  let ele = document.createElement(type);//创建第一层
  for (let key in props) {
    if (key === 'children') {
      //children可能是数组也可能是一个字符串(纯文本)
      if ({}.toString.call(props[key]).slice(8,-1)==='Array') {
        props[key].forEach(item => {
          if (typeof item === 'object') {//如果子元素是对象,那就继续调用render
            myRender(item, ele);
          } else {
            ele.appendChild(document.createTextNode(item));
          }
        })
      } else {//字符串的话直接创建文本节点并插入到元素中
        ele.appendChild(document.createTextNode(props[key]));
      }
    } else if (key === 'className') {//className要做单独处理
      ele.setAttribute('class', props[key]);
    } else {
      ele.setAttribute(key, props[key]);//其他的直接设置(style等属性未做处理)
    }
  }
  container.appendChild(ele);//把DOM元素插入到html中
  callback && callback();//=>添加成功后触发回调函数执行
};

# react中的组件

react元素是是组件组成的基本单位

组件有两种声明方式

# 1、第一种方式是函数声明

函数声明

每一个创建的组件都是一个自定义的标签,在标签上设置的属性都会转换为react对象的props属性,当react渲染DOM的时候会把props传递给下面例子中的Build函数

import React from 'react';
import ReactDOM,{render} from 'react-dom';
let school1={name:'zfpx',age:10};
let school2={name:'珠峰',age:8};
let Build = function (props) {// "函数"(组件)的参数是属性
  return <p>{props&&props.name}{props&&props.age}</p>;
};
render(<div>
  <Build name={school1.name} age={school1.age}/>
  <Build {...school2}/> {/* 利用解构赋值将对象中的内容解构出来传递给Build组件 */}
  <h1></h1>
</div>,window.root);

# 2、第二种是以类的形式声明

类声明

import React,{Component} from 'react';
import ReactDOM from 'react-dom';
let school1 = {name:'珠峰',age:8};
let school2 = {name:'珠峰',age:0};
// 1.类声明的组件有this this上有props属性
class Build extends Component{//Component这个基类上有this.setState
  render(){ // 这个组件在调用时默认会调用render方法(此render相当于注册组件)
    let {name,age} = this.props;
    return <p>{name} {age}</p>
    // return  <p>{this.props.name} {this.props.age}</p>
  }
}
ReactDOM.render(
{/*  此render的渲染步骤
1)先渲染外层div,将jsx元素转化为ES5的语法,然后调用React.createElement方法创建react对象,当遇到div下的子元素的时候,会进行以下操作
//1、发现Build 是一个继承Component的类,它会创建这个类的一个实例,并且执行原型上的render方法
//2、执行原型上的render返回JSX元素,然后继续执行外层的render
2)继续将上面返回的jsx元素转化为es5语法,调用React.createElement方法创建虚拟DOM元素对象,Build 这个位置被返回的元素对象替换
3)外层ReactDOM.render将上面生成的react对象渲染成真实的DOM结构,并追加到页面中;*/}
<div>
  <Build name={school1.name} age={school1.age}/>
  <Build {...school2} />
</div>,window.root);

区别:类声明有状态,this,和生命周期

声明组件的规则:

  • 1、首字母必须大写,目的是为了和JSX元素进行区分
  • 2、组件定义后可以像JSX元素一样进行使用
  • 3、可以通过render方法将组件渲染成真实DOM(做了优化 只会重新渲染改变的地方)

单闭合标签不能添加子节点;双闭合标签可以

# 组件中属性(props)和状态(state)的区别

区别

属性和状态的变化都会影响视图更新,但是两者导致视图刷新的机制是不一样的;

  • 1、确切的说不是因为props更新了导致的视图刷新,而是传递属性的组件重新的渲染了,从而导致接受属性的组件也重新渲染,这样属性就也随着更新了;
  • 2、状态导致的视图刷新:只要通过this.setState改变状态,视图就会刷新;

# 组件的数据来源方式

# 1、通过props(属性) 获取数据:

TIP

外界传递进来的(存在默认属性和属性校验),是只读的,不能修改;

有属性校验模块:prop-types(用来校验属性的类型和是否必须给)用yarn add prop-types来安装。用来校验属性的

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
import PropTypes from 'prop-types';
//   1、默认的属性必须写在defaultProps中(不能改名字)
//   2、校验器的名字必须是propTypes(不能改名字);
class School extends Component{//类上的属性(把类当作对象添加的属性)就叫做静态属性
  static defaultProps={//会先默认调用defaultProps
    name:'珠峰',
    age:1
  };
  static propTypes={//校验属性的值(number)类型和是否必须给(isRequired)
    age:PropTypes.number.isRequired
  };
  constructor(props){
    //如果想在constructor中使用props,则只能传递参数props,在constructor外面可以直接用this.props
    //不能在组件中更改属性
    super();
    console.log(this.props)//undefined
  }
  render(){//渲染组件的,返回一个组件
    //如果没有默认的defaultProps,也没有传递props,则this.props是一个空对象
    return <h1>{this.props.name} {this.props.age}</h1>
  }
}
//name={'我是谁'}:这样写大括号中就可以是变量
render(<School name={'我是谁'} age={9}/>,window.root);

# 2、this.state(状态) 状态是组件自己的

TIP

是可读写的(写了状态就必须给默认值); 我们可以通过this.setState({})方法来修改状态,由于修改状态会引起视图刷新(即更新DOM),这里面要经过React核心中的diff算法,最终才能决定是否进行重渲染,以及如何渲染。而且为了批次与效能的理由,多个setState有可能在执行的过程中需要被合并,所以它被设计成异步来执行时相当合理的。

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
//1、state变化可以更新视图,只能通过this.setState({})来更改状态,调用后会更新视图
class Counter extends Component{
  constructor(){
    super();
    console.log(this.state);//默认为undefined
    this.state={count:0}
  }
  add = val => {
    //setState方法会默认按最后的设置进行执行
    // this.setState({count:this.state.count+val});
    this.setState({count:this.state.count+val});//会执行这个
    //setState有两种写法,一种是传入对象的方式,一种是传入函数的方式
    //下一个状态是依赖于上一个状态时,需要写成函数的方式
    this.setState(prevState=>({count:prevState.count+val}));//如果返回的就是一个对象,则需要用'()'包裹
    //等同与下面这种写法 
    // this.setState({count:this.state.count+val},function () {
    //   this.setState({count:this.state.count+val});
    // }
  };
  render(){
    return <p>
      {this.state.count}
      <button onClick={this.add.bind(null,2)}>+</button>
    </p>
  }
}
render(<Counter/>,window.root);

# 复合组件与数据传递

复合组件:将多个组件进行组合嵌套;

复合组件之间的数据传递:

# 1、父传子

父组件传递给子组件数据通过设置属性的方式

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.css';
//1、什么是复合组件,将多个组件进行组合
//2、结构非常复杂时可以把组件分离开
//3、复合组件 有父子关系,父的数据传递给子(通过给子组件设置属性)
// Panel组件 Heading Body
class Counter extends Component{
  render(){
    //由于在Counter中传递了属性,所以这里可以用this.props获取到;
    let {header,body}=this.props;
    return (
      <div className={'container'}>
        <div className='panel-default panel'>
          {/* 这里写了之后就可以在下面的子组件中拿到对应的props值 */}
          <Header header={header}></Header>
          <Body body={body}></Body>
        </div>
      </div>
    )
  }
}

//react中需要把属性一层一层向下传递:单向数据流;
class Header extends Component{
  render(){
    return (
      <div className='panel-heading'>
      {/* 上面传递了之后就可以直接在此处使用this.props属性了 */}
        {this.props.header};
      </div>
    )
  }
}
class Body extends Component{
  render(){
    return (
      <div className={'panel-body'}>
        {this.props.body}
      </div>
    )
  }
}
let data={header:'我很帅',body:'长的英俊'};
render(<Counter {...data}/>,window.root);

# 2、子传父

通过事件的方式:父亲传递给儿子一个函数,儿子调用父亲的函数将要改的值传递给父亲,父亲更新值,刷新视图;

import React, {Component} from 'react';
import ReactDom, {render} from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
//1、子传父 通过父亲传递给儿子一个函数,儿子调用父亲的函数将要修改的值传递给父亲,父亲更新值,刷新视图;
class Counter extends Component {
  constructor() {
    super();
    this.state = {color: 'primary'};
  }
  changeColor = (color) => {
    this.setState({color});
  };
  render() {
    let {header} = this.props;
    return (
      <div className='container'>
        <div className={'panel panel-' + this.state.color}>
          <Header header={header} color={this.changeColor}></Header>
        </div>
      </div>
    )
  }
}
//react中需要把属性一层一层向下传递:单向数据流;
class Header extends Component {
  clickColor = () => {
    this.props.color('danger');//儿子调用父亲的方法,把要改的值传递给父亲,让父亲自己修改
  };
  render() {
    return (
      <div className='panel-heading'>
        {this.props.header}
        <button className={`btn btn-danger`} onClick={this.clickColor}>改颜色</button>
      </div>
    )
  }
}
let data = {header: '我很帅'};
render(<Counter {...data}/>, window.root);

# ref 属性

::: ref ref可以写在组件上也可以写在DOM元素上,获取的时候得到两者的结果是不同的;

  • 写在组件上时(这里的组件是有状态的组件),通过refs获取的是组件的实例;
  • 写在DOM元素上时,通过refs获取的是具体的DOM元素节点 :::

# ref的两种写法

# 1、直接写为一个值

通过ref设置的属性:可以通过this.refs.xxx获取到对应ref值为xxxdom元素或者组件的实例

# 2、写成一个函数的形式

写成函数时,这个函数将会在渲染成真实DOM结构或组件被挂载后执行,函数的参数为真实的DOM结构或组件的实例

//如上面非受控组件中的例子
//1、直接将ref写为一个值
<input type="text" ref='a'/>
//2、将ref写为一个函数
 <input type="text" ref={(x)=>this.c=x}/>
 {/* x 代表真实的DOM(写成函数的形式会默认传入当前设置ref的DOM结构 ),上面这种写法相当于把DOM结构挂载到了this的自定义属性c上,以后可以直接用this.c拿到这个input的DOM结构*/}
resultChange=()=>{//通过ref设置的属性:可以通过this.refs.xxx获取到对应ref值为xxx的dom元素
	this.c.value=parseFloat(this.refs.a.value)+parseFloat(this.refs.b.value);
};

# 受控组件与非受控组件

当引入HTML表单元素时,根据是否将数据托管到React组件中还是将其依然保留在DOM元素中来区分为受控与非受控组件,受控组件可以赋予默认的值

# 受控组件

指交由React控制并且所有的表单数据统一存放的组件,需要与onInput/onChange/disabed/readOnly等控制value/checked的属性或事件一起使用

import React, {Component} from 'react';
import ReactDom, {render} from 'react-dom';
class Input extends Component {
  //1、受状态控制的组件,必须要有onChange方法,否则不能使用;
  //2、受控组件可以赋予默认值(官方推荐使用受控组件)
  constructor() {
    super();
    this.state = {val: '', a: 1, b: 2};//初始化状态:设置默认值
  }
  inputChange = (val, e) => {//处理多个输入框的值映射到this.state的方法
    //val:要修改的state中的属性的名称
    //e:事件源(e.target.value是要修改成的值)
    this.setState({[val]: parseFloat(e.target.value)||0});//更改状态
  };
  render() {
    return (
      <div>
        {/* 下面两个input都受this.state影响(受控组件) */}
        <input type="text" value={this.state.a} onChange={this.inputChange.bind(null, 'a')}/>
        <input type="text" value={this.state.b} onChange={e =>{
          // 方法执行的时候要用到事件对象e,所以必须要传
          this.inputChange('b', e)
        }}/>
        {this.state.a + this.state.b}
      </div>
    )
  }
}
render(<Input/>, window.root);

# 非受控组件(基于 ref 来管理)

而非受控组件则是由DOM存放表单数据;我们可以使用refs来操控DOM元素

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
class Input extends Component{
  //输入框value值不受状态控制,不能初始化默认值
  resultChange=()=>{//通过ref设置的属性:可以通过this.refs.xxx获取到对应ref值为xxx的dom元素
    this.c.value=parseFloat(this.refs.a.value)+parseFloat(this.refs.b.value);
  };
  render(){
    return (
      <div onChange={this.resultChange}>
        <input type="text" ref='a'/>
        <input type="text" ref='b'/>
        {/*x代表真实的DOM(写成函数的形式会默认传入当前设置ref的DOM结构),下面这种写法相当于把元素挂载到了this的自定义属性c上*/}
        <input type="text" ref={(x)=>this.c=x}/>
      </div>
    )
  }
}
render(<Input/>,document.getElementById('root'));

# 受控组件与非受控组件的区别

虽然非受控组件看上去更好实现,可以直接从DOM中获取数据,而不需要额外的代码。不过实际开发中我们不建议使用非受控组件,因为更多情况下我们需要考虑到表单验证、选择性的开启或者关闭点击按钮、强制输入格式等功能的支持,而此时我们将数据托管到React中有助于我们更好的以声明式的方式完成这些功能;引入React的原因就是为了将我们从繁重的直接操作DOM中释放出来

# react 元素中的事件绑定

事件

react元素(jsx元素)事件绑定对应的方法时,方法如果不做处理,那么执行的时候方法中的this默认是undefined

可通过如下方式解决:

  • 1、使用bind方式;(绑定的时候处理)
  • 2、使用ES6的箭头函数(绑定时候处理:在外层包一层箭头函数)
  • 3、使用ES7的箭头函数(在类的原型上定义的时候处理:写为:函数名=()=>{})

# React 中的生命周期

生命周期

react操作过程中,设定了很多的执行阶段,每个阶段提供了不同方法 react-1-1

[初期渲染] defaultProps:默认属性设置与校验 constructor:获取属性和设置默认初始状态 componentWillMount(只执行一次)constructor执行完成后,DOM结构开始加载之前,执行此方法, render(开始渲染) componentDidMount(只执行一次)DOM结构加载完成后会自动触发此函数

[状态发生改变时触发] shouldComponentUpdate:状态更新时会触发此方法,返回布尔值 componentWillUpdate:如果shouldComponentUpdate返回true则执行此函数,否则不执行 render(开始渲染) componentDidUpdate:组件完成更新后触发此方法

[卸载组件时触发] ReactDOM.unmountComponentAtNode:删除某个组件 componentWillUnmount(组件将要卸载):组件移除时会调用此方法,一般在这个方法中我们要清除定时器

[属性发生改变时触发] componentWillReceiveProps

import React, {Component, PureComponent} from 'react';
import ReactDom, {render} from 'react-dom';
class Counter extends Component {/*PureComponent是纯组件:比较的是状态的地址,如果是同一个地址,则不会更新,所以状态最好采用新的状态替换掉老的*/
  static defaultProps = {
    name: 'zfpx'
  };
  constructor(props) {
    super();
    this.state = {number: 0};
    console.log('1、constructor');
  }
  componentWillMount() {//同步代码放在此处最好:如获取本地的数据:在渲染之前获取数据,只渲染一次
    console.log('2、父组件将要加载');
  }
  componentDidMount() {
    this.setState({number: this.state.number + 1});
    console.log('5、父组件已经挂载完成');
  }
  click = (e) => {
    this.setState({number: this.state.number + 1});
  };
  //react可以在shouldComponentUpdate方法中优化 PureComponent 可以帮我们做这件事
  shouldComponentUpdate(nextProps, nextState) {//分别代表下一次的属性和下一次的状态
    //组件是否需要更新
    console.log('父组件是否需要更新');
    return nextState.number % 2;//如果此函数返回了false,就不会调用render方法了
  }
  //乱用this.setState({})会造成栈溢出
  componentWillUpdate() {
    console.log('父组件将要更新');
  }
  componentDidUpdate() {
    console.log('父组件完成更新');
  }
  render() {
    console.log(`3、父组件render(渲染)`);
    return (
      <div>
        <p>{this.state.number}</p>
        <ChildCounter n={this.state.number}/>
        <button onClick={this.click}>+</button>
      </div>
    )
  }
}

class ChildCounter extends Component {
  componentWillReceiveProps(newProps) {//第一次不会执行,之后属性更新时才会执行
    console.log('子有新的属性');
  }
  render() {
    console.log('4、child render');
    return (
      <div>
        {this.props.n}
      </div>
    )
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log('子组件是否需要更新');
    return nextProps.n % 3;
  }
}
render(<Counter name={'计数器'}/>, window.root);

注意:

最好不要在任何一个状态更新时触发的方法中写this.setState(),如果不加条件控制的话,会再次触发状态更新而导致方法执行,这样就会造成栈溢出;

/*页面初始化时触发*/
defaultProps
constructor
componentWillMount  //(将要挂载)页面初次加载时执行1次
render
componentDidMount   // (完成挂载)页面初次加载完成时执行1次

/*状态更新时会触发*/
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

/*属性更新*/
componentWillReceiveProps

/*卸载*/
componentWillUnmount

# 获取组件的所有子节点

获取子节点

在函数式声明的组件中我们用props.children来获取组件的所有子节点; 在以类声明的组件中我们用this.props.children来获取组件的所有子节点;

render(<Dinner>
  <div>吃啥</div><div>吃啥</div>{ /* 此处相当于组件的子节点 */ }
</Dinner>,window.root);

this.props.children的值有三种情况:

  • 1、如果组件中间没有子节点,值是undefined
  • 2、如果有一个子节点,获取的是对象数据类型的值
  • 3、如果有多个子节点,获取的是数组类型的值

我们可以用React.Children.map去遍历this.props.children获取到的结果

import React from 'react';
import ReactDom,{render} from 'react-dom';
//this.props.children是获取组件中间的内容
//有三种可能的值:
// 1、不传的话默认是undefined;
// 2、传一个时是对象类型
// 3、传多个是数组类型
//我们可以用React.Children.map去遍历this.props.children
export default class Dinner extends React.Component {
  render(){
    return (
      <div>
        {React.Children.map(this.props.children,(item,index)=>(
          <li>{item}</li>
        ))}
      </div>
    )
  }
}
render(<Dinner>
  <div>吃啥</div><div>吃啥</div>{/*此处相当于组件中间的内容*/}
</Dinner>,window.root);
上次更新: 5/19/2020, 11:40:50 PM