react组件

181 阅读7分钟

安装

使用 create-react-app 快速构建 React 开发环境

create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

create-react-app 自动创建的项目是基于 Webpack + ES6

yarn add global creat-react-app

creat-react-app xxxxxx为文件夹名称

cd xxx

安装好之后就会提示已经帮忙安装git、可以使用yarn start等命令

cdn 连接

如果想要使用cdn连接来方便学习,那么复制以下代码

    <script src="https://cdn.bootcss.com/react/16.10.2/umd/react.development.js"></script>
    <script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.development.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/7.0.0-beta.3/babel.min.js"></script>

把script标签改成

<script type="text/babel">

上面的方式是通过cdn连接到react的在线云文件(react和reactDOM)以及babel-browser

react.js 是 React 的核心库,

react-dom.js 是提供与 DOM 相关的功能,

babel.js 的作用是将 JSX 语法转为 JavaScript 语法

凡是使用 JSX 的地方,都要加上 type="text/babel" 。

ReactDOM.render()

ReactDOM.render()是react的基本语法,用来将模板做成html语言,并且放置到对应的dom节点里

    <div id="example"></div>
 ReactDOM.render(
        <h1>helloworld</h1>,
        document.querySelector("#example")
      )

上面的代码将模板放到id为example的div标签内。

JSX语法

JSX语法为js的扩展语言,是react编写的比较有名的模板写法,类似于vue文件中的的template标签,只不过vue是用了自己编写的vue-loader来解析,而JSX语法是通过babel-loader来解析的。webpack内置babel-loader,所以我们无需做配置就可以yarn start

JSX语法最大的特点就是支持html和js混写,只需要用{}包裹js语言

有一个地方需要注意,就是 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

示例

var names = ["apple", "banana", "orange"];
ReactDOM.render(
  <ul>
    {names.map((value) => {
      return <li>{value}</li>;
    })}
  </ul>,
  document.getElementById("example")
);

上面的代码采用js跟html混写的方式,把虚拟dom插入到ul上面,就跟v-for一样方便

我们甚至还可以写一点if语句

let li;
if (true) {
  li = <li>123</li>;
}
ReactDOM.render(<ul>{li}</ul>,   
document.getElementById("example")
);

还可以这样改进

ReactDOM.render(<ul>

{2 > 1 ? <li>123</li> : <li>456</li>}

</ul>,document.getElementById("example" )

上面的代码都是一样将<li>123</li>渲染进ul标签内

使用组件

类组件

以前的React 使用 React.createClass ()创建组件类

现在我们直接可以使用class 组件名 extends React.Component{}的方式来定义组件,并且直接使用<组件名 />的方式来使用它

下面是示例

class Mycomponent extends React.Component {//定义组件
  render() {
    return <div>this is component</div>;
  }
}
ReactDOM.render(
  <div>
    this is parent
    <Mycomponent /> //使用组件
  </div>,
  document.querySelector("#example")
);

函数组件

使用函数组件在某种程度上比类组件更便捷

function Mycomponent() {
  return <div>this is component</div>;
}

两者的效果是一样的

组件的外部数据props

类组件

this.props不但可以用来传递数据,还包含了组件的所有信息,以下代码读取了它的value值。

class Mycomponent extends React.Component {
  render() {
    return (
      <div>
        传进来的props是{typeof this.props.value}  //string
        {JSON.parse(this.props.value).map((value) => {
          return <div>{value}</div>;  //由于是string所以转一下
        })}
      </div>
    );
  }
}
ReactDOM.render(
  <div>
    this is parent
    <Mycomponent value="[1,2,3]" />//这里传递了数据
  </div>,
  document.querySelector("#example")
);

上面的代码中,在使用组件的地方传递了value,然后组件处使用this.props接收,由于传递进来的数据是字符串,所以使用JSON.parse把它变成了数组,然后再在子组件上添加了三个div,效果如下

函数组件

function Mycomponent(props) {
  return (
    <div>
      传进来的props是{typeof props.value}
      {JSON.parse(props.value).map((value) => {
        return <div>{value}</div>; //由于是string所以转一下
      })}
    </div>
  );
}

实际上两者相差不大,类组件用的是this,而函数组件是通过显性传递props来获取到组件信息

两者写法的区别

函数组件

//定义组件
function Mycomponent(props) {
  return <div>{props.value}</div>;
}
//使用组件
<Mycomponent value="1"/>

类组件

//定义组件
class Mycomponent extends React.Component {
  render() {
    return <div>{this.props.value}</div>;  
  }
}
//使用组件
<Mycomponent value="1"/>

组件的注意要点

组件必须使用首字母大写的形式,且一个组件只能有一个根节点

// the first letter of the component's name must be capitalized
// so name must be HelloMessage
class helloMessage extends React.Component {
  render() {
    return <h1> 
      Hello {this.props.name}
    </h1><p>   // wrong,should only one top child node
      some text
    </p>;
  }
}

this.props.children

有时候我们需要给组件增加子节点,比如这样

    <Mycomponent className="shy">
      <div>子节点1</div>
      <div>子节点2</div>
    </Mycomponent>

但是这段代码这样写,对组件是无效的。我们可以通过this.props.children来获取他的子节点,然后渲染进组件内

class Mycomponent extends React.Component {
  render() {
    return (
      <ol className="component">
        {React.Children.map(this.props.children, (child) => {
          return <li>{child}</li>;
        })}
      </ol>
    );
  }
}

可以看到代码已经渲染进来了 这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ;如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。

React提供一个React.Children.map的方法来帮助我们处理this.props.children,通过这个方法我们可以遍历组件的子节点

内部数据state

class和函数式组件的state写法是不一样的,但是它们都遵循同一套逻辑,我们目前需要了解的是怎么初始化数据、如何get数据、如何set数据

class组件

class Mycomponent extends React.Component {
  constructor() {
    super();  //这里调用super继承父类的属性 固定写法
    this.state = {  //这里使用this.state初始化内部数据
      n: 1,
      m: 2
    };
  }
  render() {
    return (
      <div>
        这里是内部数据n:{this.state.n}  //读数据使用this.state
        这里是内部数据m:{this.state.m}
      </div>
    );
  }
}

函数组件

const Mycomponent = () => {
  const [n, setN] = React.useState(1);//数组析构写法 
  const [m, setm] = React.useState(2);
  return (
    <div>
      内部数据n:{n}  //读数据
      内部数据m:{m}
    </div>
  );
};

上面的数组析构写法意思是把React.useState产生的数组分别赋给nsetN

其中React.useState(1)用来初始化数据

n是读取时用的

这个setN是什么?实际上它是用来set数据的,这一点我们在下一节再谈

setState

函数和class组件下的setState自然也是不同的,要注意的是react的数据并不是双向绑定的,也不会像vue一样对数据进行gettersetter的监听。

class组件下的setState

class下使用的是this.setState()方法来设置新的state,要注意这里的setState方法是异步执行的

render() {
    return (
      <div className="Son">
        <button onClick={() => this.add()}>+1</button>
      </div>
    );
  }
  add() {
    this.setState((state) => {
      return { n: this.state.n + 1 };
    //也可以写成
    //this.setState({n:this.state.n+1})
    // 但推荐setState((state)=>{return {...}})的写法
    });
  }

类组件注意事项

  • 正如你所见,在类组件下,我们要更新state时,使用this.state.n+1是有效的,只不过不会像vue一样渲染到页面上,只有调用setState才有效,这是因为react没有像vue一样监听data数据的机制。
  • 使用setState方法最好在里面套用一个回调函数,因为它是异步的,所以不会马上修改state。假如我们要使用新数据,那么不适用回调函数的情况下只能获得旧的state
  • react不希望我们修改原state,更希望能返回一个新的state,这时基于react的数据不可变思想,这点跟vue2非常不同。

函数组件下的setState

在函数组件下使用的是上面例子里 const [n, setN] = React.useState(1)中的setN()函数进行修改。React.useState方法初始化数据并返回一个数组对象,一共有两个value,第一个是初始化的数据,第二个是设置它的方法。

const [n, setN] = React.useState(0);//变量名随便取
  return (
    <div>
      {n}
      <button onClick={() => setN(n + 1)}>+1</button>
    </div>
  );

要注意setN**不会改变**state的值,而是返回一个新的state。

函数组件注意事项

  • 在函数组件下,没有this这一说,一切内容都是使用参数和函数
  • 它是使用setxx()函数的形式来更新UI的

复杂类型下的state处理

或许有人已经注意到了类组件和函数组件的state区别

类组件

this.state={
n:1,
m:2
}

函数组件

 const [n, setN] = React.useState(1);
 const [m, setm] = React.useState(2);

我们能不能把函数组件的修改成像类组件一样方便呢?答案是可以的,比如说修改成

 const [state, setState] = React.useState({n:1,m:2});

但是如果你要通过点击事件对源数据进行修改

//这里的写法也要跟类组件一样
<button onClick={() => setState({ n: state.n + 1 })}>+1</button>
<button onClick={() => setState({ m: state.m + 1 })}>+1</button>

你会发现修改了n,m就会消失,这是因为类组件下的this.state这个对象,里面的数据会自动合并,也就是react帮我们做了了以下处理:

this.state={m:1,n:1}//原数据
//当让this.state.m + 1时 实际上this.state已经变成新对象了。
this.state= ...return {...this.state,m:this.state.m+1}
//react帮助我们拷贝了原来的state里面的数据{m:1,n:1}
//然后又给了新的m,对原来的数据进行了覆盖,此时this.state为{m:2,n:1}

上面的代码中,...this.state的意思是展开了原来state对象,把里面的属性都给了return后的对象

上面我们知道了类组件state的原理,而函数组件下的state并不会自动帮助我们合并,所以我们要写成

 <button onClick={(state) => setState({ ...state,n: state.n + 1 })}>+1</button>
<button onClick={(state) => setState({...state,m: state.m + 1 })}>+1</button>

重点是...state,拷贝原来的对象里面的数据

参考文档

zh-hans.reactjs.org/docs/hooks-…