React.createElement()

这是初学React时重要的api,第一个参数为要创建的元素,第二个参数为对象接收类名或绑定事件,第三个参数开始接收子元素。
截屏2020-05-16 下午1.27.22.png
如上,我们借助React提供的CreateElement和render函数,可以很简单的生成视图与标签,不用再像原生JS那样手动获取dom再操作dom。下面动手来优化这个代码。
截屏2020-05-16 下午3.23.43.png
我们很容易的把render函数中临时变量替换掉,减少重复代码。优化到这步,我们发现div的结构和HTML代码很相似,如下
截屏2020-05-16 下午3.27.04.png
我们现在知道,上图中从下面到上面的语法我们称之为JSX,并且Babel支持将JSX转译为React语法。

jsx

jsx其实就是javascript的一种扩展,其最终会被编译为js,书写起来就像html一样。注意如下:

  1. React DOM 使用 classNamehtmlFor 来对应class与for;
  2. jsx接收表达式,表达式用{}括起来。中间可以是三元运算等等;
  3. jsx的样式常用内连的方式,如下
    1. var myStyle = {
    2. fontSize: 100,
    3. color: '#FF0000'
    4. };
    5. ReactDOM.render(
    6. <h1 style = {myStyle}>菜鸟教程</h1>,
    7. document.getElementById('example')
    8. );
    截屏2020-05-16 下午5.03.43.png

    使用Class语法

  • 用class的方式来表达组件,可以隔离作用域,使每个函数组件都有自己的状态。
  • 如果组件内部非常纯净,没有需要维护的状态,就可以使用function,反之,使用Class。 ```javascript // 下面这两种组件是完全一样的 // 同时注意,ES6 Class里面可以直接使用props对象参数 function Welcome(props) { return

    Hello, {props.name}

    ; }

class Welcome extends React.Component { render() { return

Hello, {this.props.name}

; } }

  1. - 完整例子如下
  2. ```javascript
  3. function App(props){
  4. return (
  5. <div>
  6. <Box1 name="frank"/>
  7. <Box2 name="jack"/>
  8. </div>
  9. )
  10. }
  11. class Box1 extends React.Component{
  12. constructor(props){
  13. super(props)
  14. this.state = { // 在constructor中初始化state
  15. number: 0
  16. }
  17. }
  18. add(){
  19. this.setState({
  20. number: this.state.number + 1
  21. })
  22. }
  23. minus(){
  24. this.setState({
  25. number: this.state.number - 1
  26. })
  27. this.state.number -= 1
  28. render()
  29. }
  30. render(){ // 在render 函数中访问state的数据
  31. return (
  32. <div className="red">
  33. <span>{this.state.number}</span>
  34. <button onClick={this.add.bind(this)}>+</button>
  35. <button onClick={this.minus.bind(this)}>-</button>
  36. {this.props.name}
  37. </div>
  38. )
  39. }
  40. }
  41. class Box2 extends React.Component{
  42. constructor(props){
  43. super(props)
  44. this.state = {
  45. number: 0
  46. }
  47. }
  48. add(){
  49. this.setState({
  50. number: this.state.number + 2
  51. })
  52. }
  53. minus(){
  54. this.setState({
  55. number: this.state.number - 2
  56. })
  57. }
  58. render(){
  59. return (
  60. <div className="red">
  61. <span>{this.state.number}</span>
  62. <button onClick={this.add.bind(this)}>+2</button>
  63. <button onClick={this.minus.bind(this)}>-2</button>
  64. {this.props.name}
  65. </div>
  66. )
  67. }
  68. }
  69. render()
  70. function render(){
  71. ReactDOM.render(
  72. <App/>, // React.createElement(App)
  73. document.querySelector('#root')
  74. )
  75. }
  • setstate会自动调用render,执行视图更新,且只会刷新更改的部分。
  • https://jsbin.com/nezocuk/2/edit?html,js,output

    state

    我们已经知道React的组件实际上就是一个函数,而state实质上是函数的私有变量,外部其他组件或者方法都是无法直接访问到内部的state。 而state主要被设计用于维持组件内部私有状态。注意:不能修改state,而必须通过setState!否则直接修改并不会触发组件的render。

    setState

    用来修改state,如果我们按照直觉,在一个方法内直接修改state的状态,如**this**``.state.time = (+``**new**`` ``Date``()),这样其实是不会带来视图的改变的!

React对于setState的定义为请求React修改某个数据,而React的实现则是将对变量的修改放入一个修改队列中,在一个循环之后进行批量更新结果(深入点涉及VDom的更新机制)。所以,这里会造成一个问题,就是setState数据之后立刻进行读取,可能你读取到的数据,并非是已经被更新过的有效值。

setState有多种使用方式,如下:

  1. 直接赋值 ```javascript import React, { PureComponent } from ‘react’

export default class index extends PureComponent { constructor(props){ super(props) this.state = {name:’demo react’,time:+new Date()} } handleUpdateName () { // this.state.time = (+new Date()) console.log(‘修改前的值’,this.state.time) this.setState({time:+new Date()}) // 此处直接赋值 console.log(‘修改后的值’,this.state.time) let that = this setTimeout(function(){ console.log(‘当前state值’,that.state.time) }) } render() { return (

Hello world React!{this.state.name}

组件生成时间:{this.state.time}

) } }

  1. 点击按钮,控制台输出了不同的值,可以观察到`setState`是**采用异步队列**模式的,也就是说多个setState()调用有可能被合并为一次更新,所以不能依赖它的值计算下一次state的更新,记住异步的坑!<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/125229/1589734321567-2db08d56-c675-420d-b6b4-f4677b2ab918.png#align=left&display=inline&height=132&margin=%5Bobject%20Object%5D&name=image.png&originHeight=264&originWidth=616&size=57024&status=done&style=none&width=308)
  2. 2. **赋值完成等待同步完成之后执行回调**
  3. 现在出现了一个问题,如果我们需要通过等待`setState`修改完成的值之后,应该如何处理?React为我们的`setState`提供了第二个参数`callback`
  4. ```javascript
  5. import React, { PureComponent } from 'react'
  6. export default class index extends PureComponent {
  7. constructor(props){
  8. super(props)
  9. this.state = {name:'demo react',time:+new Date()}
  10. }
  11. handleUpdateName () {
  12. // this.state.time = (+new Date())
  13. console.log('修改前的值',this.state.time)
  14. this.setState({time: +new Date()},(function(){ // 回调函数将在设置状态成功后执行,可以获取最新的state值
  15. console.log('当前state值',that.state.time)
  16. })
  17. console.log('修改后的值',this.state.time)
  18. }
  19. render() {
  20. return (
  21. <div>
  22. Hello world React!{this.state.name}
  23. <p>组件生成时间:{this.state.time}</p>
  24. <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
  25. </div>
  26. )
  27. }
  28. }

再次运行,并且点击按钮。我们可以看到控制台输出的值是跟第一个方法是一致的。
image.png

  1. 通过原始数据进行更新

在某些场景下面,我们可能是新的值是基于上一次的值推算而来,所以React提供了setState传递进方法来进行推算处理。他接受两个参数,第一个为前一个状态,第二个参数为应用更新后的状态(props),如下:

  1. // 不可取
  2. this.setState({
  3. counter: this.state.counter + this.props.increment,
  4. });
  5. this.setState((prevState, props) => ({
  6. counter: prevState.counter + props.increment
  7. }));
  1. import React, { PureComponent } from "react";
  2. export default class index extends PureComponent {
  3. constructor(props) {
  4. super(props);
  5. this.state = { name: "demo react", time: +new Date() };
  6. }
  7. handleUpdateName() {
  8. console.log("修改前的值", this.state.time);
  9. let that = this;
  10. this.setState(oldData => {
  11. return { time: oldData.time + 1000 };
  12. });
  13. console.log("修改后的值", this.state.time);
  14. setTimeout(function() {
  15. console.log("当前state值", that.state.time);
  16. });
  17. }
  18. render() {
  19. return (
  20. <div>
  21. Hello world React!{this.state.name}
  22. <p>组件生成时间:{this.state.time}</p>
  23. <button onClick={this.handleUpdateName.bind(this)}>修改值</button>
  24. </div>
  25. );
  26. }
  27. }

props

我们知道,在函数中有带参数的方法,那么props其实就是传入方法中的参数。并且在React中props是只读属性。在使用场景上,是由父组件向子组件传递值的时候使用的。

  1. import React, { PureComponent } from "react";
  2. export default class index extends PureComponent {
  3. constructor(props) {
  4. super(props);
  5. }
  6. render() {
  7. return (
  8. <div style={{'background':'#fefefe'}}>
  9. {this.props.value||'暂无数据'}
  10. </div>
  11. );
  12. }
  13. }

多层props传值,在 ReactDOM.render 定义属性值,传给调用方法 App,再在调用的ES6类调用中用 props.属性直接赋值过去。

  1. var myStyle = {color:'red',textAlign:'center'}
  2. class Name extends React.Component {
  3. render() {
  4. return <h1 style={myStyle}>网站名称:{this.props.name}</h1>;
  5. }
  6. }
  7. class Url extends React.Component {
  8. render() {
  9. return <h1>网站地址:{this.props.url}</h1>;
  10. }
  11. }
  12. class Nickname extends React.Component {
  13. render() {
  14. return <h1>网站地址:{this.props.nickname}</h1>;
  15. }
  16. }
  17. function App(props) {
  18. return (
  19. <div>
  20. <Name name={props.name}/>
  21. <Url url={props.url}/>
  22. <Nickname nickname={props.nickname}/>
  23. </div>
  24. );
  25. }
  26. ReactDOM.render(
  27. <App name={"菜鸟教程"} url={"http://www.runoob.com"} nickname={"Runoob"}/>,
  28. document.getElementById('example')
  29. );

如何向子组件传值呢?就像给html标签增加属性一样。

  1. <Content value={'我设置了' + this.state.time}/>

这样,组件内部可以通过props读取到value值了。不过React的组件传递中有一个很有趣的属性children,这个的用处传递组件包含的内容。

  • children:这个的用处传递组件包含的内容。 ```javascript // index.js主体Children

// app.js import React, { PureComponent } from “react”;

export default class index extends PureComponent { constructor(props) { super(props); }

render() { return (

{this.props.value||’暂无数据’},children:{this.props.children}
); } }

  1. <a name="KGK4a"></a>
  2. ### props和state的区别
  3. [https://www.jianshu.com/p/255085c0ddd9](https://www.jianshu.com/p/255085c0ddd9)
  4. - props是指组件间传递的一种方式,props自然也可以传递state。由于React的数据流是自上而下的,所以是从父组件向子组件进行传递;另外组件内部的this.props属性是只读的不可修改!
  5. - state是组件内部的状态(数据),不能够直接修改,必须要通过setState来改变值的状态,从而达到更新组件内部数据的作用。
  6. <a name="KSOVw"></a>
  7. ###### props和state是经常要结合使用的,父组件的state可以转化为props来为子组件进行传值。在这种情况下,子组件接收的props是只读的,想要改变值,只能通过父组件的state对其进行更改。<br />
  8. <a name="tFtvY"></a>
  9. ### 父子组件通信
  10. - 父子元素通信:父元素传一个函数给子元素,子元素调用父元素,调用时可以传递一些额外数据,父元素在子元素上传递事件函数,如`<Track success={this.success1.bind(this)}></Track`,子元素内可以这样调用传递的函数,如`this.props.success('我是参数');`这样就可以调用父元素的success1函数并传递参数,如`success (x) { ...}`
  11. ```javascript
  12. class App extends React.Component {
  13. constructor(props) {
  14. super(props)
  15. this.state = {
  16. msg: '你好'
  17. }
  18. }
  19. changeMsg() {
  20. console.log('函数被调用')
  21. this.setState({msg: '真好'} ,() => {
  22. console.log('修改完毕')
  23. })
  24. }
  25. render() {
  26. return (
  27. <div>
  28. <Box msg={this.state.msg} fn={this.changeMsg.bind(this)} />
  29. </div>
  30. )
  31. }
  32. }
  33. function Box(props) {
  34. return (
  35. <div>
  36. <p>我是子组件,我接收到的value是 {props.msg}</p>
  37. <button onClick={props.fn}>改变</button>
  38. </div>
  39. )
  40. }
  41. ReactDOM.render(<App />, document.querySelector('#app'));
  • 爷孙组件通信:爷爷给爸爸,爸爸再给孙子

    任意两个组件通信:eventbus

    发布订阅模式

    1. var lists = {}
    2. var eventHub = {
    3. on (eventName, fn) { // 订阅
    4. if (!lists(eventName)) {
    5. lists[eventName] = {};
    6. }
    7. lists[eventName].push(fn); // 订阅某个事件后,将事件回调函数推入列表中
    8. },
    9. trigger (eventName, data) {
    10. let list = lists[eventName];
    11. if (!list) return;
    12. for(let i = 0;i < lists.length;i++) {
    13. lists[i](data); // 执行列表中所有回调函数
    14. }
    15. }
    16. }

    redux

  • react.createStore(fn),接受一个函数,函数接受两个参数,state和action,函数返回值是新的状态对象

  • 在顶层组件中<App store={store.getState()} />来获取初始的state;
  • 触发一个action,相当于发布订阅里的trigger,发布一个事件:store.dispatch({type: '我是一个action', payload: '传递的数据'});
  • 触发事件后,由reducers来监听,其本质就是一个函数,相当于管家:let reducers = (state, action) => {…},state为之前的state,action为传递的action,包含type与payload,该函数返回修改的数据

redux的优点

  • 将event归类
  • 所有的数据都是只读的,不能去修改根组件的数据,虽然你无法阻止

    在vanilla中使用redux

    截屏2020-05-23 上午10.25.01.png
    如上,redux的核心逻辑就三步。

    react + redux

    记住,组件改变state的唯一方法是通过调用store的dispatch方法(派发行为action给store),触发一个action,这个action被对应的reducer处理,于是state完成更新。其它组件可以通过订阅store中的状态(state)来刷新自己的视图。