快速上手react

概况:

通过本篇文章你可以对react的重点有个整体的认识。
关于react是什么,优点,解决什么问题等,网上一大推就不啰嗦了。
了解虚拟DOM的实现,参考这边文章
虚拟DOM
不想看的话简单讲,其实就是用一个轻量级的dom结构(用js模拟实现),来模拟重量级的dom结构,通过比对轻量级结构后,在操作重量级dom结构提高性能,从而到达性能优化的目的。

生命周期:

快速学习react 先了解它的重中之重----生命周期,
一个组件在不同时期会调用不同时期的函数接口也就是对应的生命周期函数

装载时期的函数

getDefaultProps(是设置默认props)getInitialState(废弃,设置默认State)
依次执行以下函数
• constructor
• componentWillMount
• render
• componentDidMount

更新时期的函数

如果组件的数据有变化了(porp,state), 依次执行以下函数
• componentWillReceiveProps
• shouldComponentUpdate
• componentWillUpdate
• render
• componentDidUpdate

卸载时期的函数

销毁组件
•componentWillUnmount

import React,{ Component } from 'react';

class Demo extends Component {
  constructor(props) {
    // 构造函数,要创造一个组件类的实例,会调用对应的构造函数,
    //一个react组件需要构造函数,往往为了两个目的.
    //1:初始化state.2.绑定成员函数this环境。  
    // 无状态的函数就不需要构造函数,
      super(props)
      console.log("---初始化组件---")
      this.state = {
        test:'想要显示一段不一样的文字'
         //定义state,存放页面的数据,通过this.setState()方法修改
        //.this.setState()函数所做的事情,首先是改变this.state的值,然后驱动组件经历更新过程,这样才有机会让this.state里新的值出现在界面上。
      }
  }
componentWillMount () {
    console.log("---组件挂载前---最先执行的函数")
   
}
componentDidMount () {
    console.log("---组件挂载后---")
}
componentWillReceiveProps (nextProps) {
    console.log("---父组件重新渲染---")
值得注意的是,更新时期的componentWillReceiveProps函数,
只要父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程,不管父组件传给子组件的Props有没有改变,都会触发子组件的componentWillReceiveProps函数,但是自身的this.setState方法触发的更新过程不会调用这个函数。
}
shouldComponentUpdate (nextProps,nextState) {
    console.log("---组件接受到重绘状态---")
    它决定了一个组件什么时候不渲染。
    在更新过程中shouldComponemtUpdata 返回 false那就立刻停止更新。
    this.setState函数后会执行shouldComponemtUpdata 然后在决定我要不要更新。
  相反 shouldComponemtUpdata 函数返回 TRUE,接下来就会依次调用
   componentWillUpdata,render,componetDidUpdata函数,它把render像夹心面包似得夹在了中间。
}
componentWillUpdate (nextProps,nextState) {
  console.log("---组件将要更新---")
} 
 componentDidUpdate (prevProps,prevState) {
   console.log("---组件更新完毕---")
}
render () {
    console.log("---组件渲染---")
    return (
        <div>{this.state.test}</div>
    )
}
componentWillUnmount () {
   console.log("---组件销毁---")
}
}
export default Demo;

componentWillMount 和componentDidMount的区别:componentWillMount 可以在服务器调用,也可以在浏览器调用但是componentDidMount只能在浏览器被调用,因为装载是一个组件放到DOM树上的过程,那么真正的装载是不可能在服务器上完成的,服务器的渲染并不会产生DOM树。所以我们可以利用这一点。在componentDidMount被调用时候,组件已经被装载到DOM树上了,可放心的去操作渲染出来的任何DOM。

编写组件:

组件间的传递通过props进行传递,看下面例子

import React from 'react';
// 一级父组件
class Level1 extends React.Component{
    render(){
        return  <Level2 color='red'/>
    }
}
// 二级子组件
class Level2 extends React.Component{
    render(){
        return  <Level3 color={this.props.color}/>
    }
}
// 三级孙子组件
class Level3 extends React.Component{
    render(){
        return  <div color={{color: this.props.color}}/>
    }
}

也可以这样创建

import React from 'react';
const Level1 = React.createClass({  
  render() {
    return (
      <div></div>
    );
  }
});
export default Level1 ;  
React.createClass和extends Component的区别:

Component{}是ES6的写法,会自动继承Component里面的属性
createClass({})是React的ES5写法,会生成一个React Component
语法区别
• propType 和 getDefaultProps
• 状态的区别
• this区别
• Mixins
参考这篇文章
React.createClass和extends Component的区别
如果你的组件是无状态的,纯输出组件也可以直接写成函数如下

function Pure(props){
   return(
       <div>{props.xxxx}</div>
   )
}

react 组件必须一级一级传递


clipboard.png

如果想要越级传递,1直接到5,那么需要用到redux

redux

redux之前最好了解下flux,但是redux更加优秀。
react和redux事实上是两个独立的东西,如果你两者单独使用推荐react-redux库,我们从redux 使用方法开始,循序渐进过渡到react-redux,这个库可以让我们简化代码的书写

在redux框架下,一个react组件是这样运行的

读取Store的状态,用于初始化组件的状态,同时监听Store的状态改变,当Store状态发生改变时候,就需要更新组件的状态,从而驱动渲染。当需要更新store状态时,就要派发action对象。
根据当前props,和state,渲染用户界面。

项目结构

actions--->用户行为
components--->组件
containers--->容器
reducer--->一个纯函数返回新的state状态
store--> store里面负责分发action行为
index.html ---> 模板文件
webpack---> 打包配置文件

actions:

• 是一个行为的抽象
• 是普通JS对象
• 一般由方法生成
• 必须有一个type
我要添加一本书这个行为可以如下:

const addTodo = (text) =>{
    retrun {
       type:'Add',
       id: nextTodoId++,
       text,
    }
}

reducer:
• 是响应的抽象
• 是纯方法
• 传入旧的状态和action
• 返回新的状态

签名函数:reducer(state, action) state 是当前状态,action是接受到的action,
注意不能改变参数state和action

const todo = (state, action) =>{
    switch (action.type){
    case "Add_Book": 
     return
      {
          text: action.text,
     }
}

用一个例子串起来:

设计一个具有加减功能的项目:

Actions.js:

export const increment = (counterCaption) => {
  return {
    type: increment,
    counterCaption: counterCaption
  };
};

export const decrement = (counterCaption) => {
  return {
    type: decrement,
    counterCaption,//es6写法等同于counterCaption: counterCaption
  };
};

Reducer.js:

export default (state, action) => {
  const {counterCaption} = action;//等同于const counterCaption= action.counterCaption;

  switch (action.type) {
    case increment:
      return {...state, [counterCaption]: state[counterCaption] + 1};
    case decrement:
      return {...state, [counterCaption]: state[counterCaption] - 1};

    default:
      return state
  }
}

//return {...state, [counterCaption]: state[counterCaption] - 1};等同于
//const newState = Object.assign({},state);
//newState[counterCaption]--;
//return newState;

Store.js:

import {createStore} from 'redux';
import reducer from './Reducer.js';

const initValues = {
  'First': 0,
  'Second': 10,
  'Third': 20
};

const store = createStore(reducer, initValues);

export default store;

//createStore是redux库提供的函数第一个参数是更新状态的reducer,第二参数是初始值

views(容器):

import React, { Component } from 'react';
import Counter from './Counter.js';

class ControlPanel extends Component {
  render() {
    return (
      <div>
        <Counter caption="First" />
        <Counter caption="Second" />
        <Counter caption="Third" />
      </div>
    );
  }
}
export default ControlPanel;

Counter.js(组件):

import React, { Component, PropTypes } from 'react';

import store from '../Store.js';
import * as Actions from '../Actions.js';

const buttonStyle = {
  margin: '10px'
};

class Counter extends Component {
  render() {
    const {caption, onIncrement, onDecrement, value} = this.props;

    return (
      <div>
        <button style={buttonStyle} onClick={onIncrement}>+</button>
        <button style={buttonStyle} onClick={onDecrement}>-</button>
        <span>{caption} count: {value}</span>
      </div>
    );
  }
}
//以下是对参数类型的定义,开启eslint需要写一下代码。
Counter.propTypes = {
  caption: PropTypes.string.isRequired,//表示caption是string类型,必填
  onIncrement: PropTypes.func.isRequired,
  onDecrement: PropTypes.func.isRequired,
  value: PropTypes.number.isRequired
};


class CounterContainer extends Component {
  constructor(props) {
    super(props);

    this.onIncrement = this.onIncrement.bind(this);
    this.onDecrement = this.onDecrement.bind(this);
    this.onChange = this.onChange.bind(this);
    this.getOwnState = this.getOwnState.bind(this);

    this.state = this.getOwnState();
  }

  getOwnState() {
    return {
      value: store.getState()[this.props.caption]
    };
  }

  onIncrement() {
    store.dispatch(Actions.increment(this.props.caption));
  }

  onDecrement() {
    store.dispatch(Actions.decrement(this.props.caption));
  }

  onChange() {
   //为了保持Store上的状态和this.state的同步
    this.setState(this.getOwnState());
  }

  shouldComponentUpdate(nextProps, nextState) {
    return (nextProps.caption !== this.props.caption) ||
      (nextState.value !== this.state.value);
  }

  componentDidMount() {
   //为了保持Store上的状态和this.state的同步
    store.subscribe(this.onChange);
  }

  componentWillUnmount() {
    //为了保持Store上的状态和this.state的同步
    store.unsubscribe(this.onChange);
  }

  render() {
 //Counter 在上面
    return <Counter caption={this.props.caption} 
      onIncrement={this.onIncrement}
      onDecrement={this.onDecrement}
      value={this.state.value} />
  }
}

CounterContainer.propTypes = {
  caption: PropTypes.string.isRequired
};

export default CounterContainer;

通常我们会把容器放在container文件夹下,把组件放在component下


clipboard.png

clipboard.png

ControlPanel 根本就没有使用store,如果仅仅为了传递prop给组件counter就要求支持state prop,显然不合理,其中react提供了Context的功能可以解决这个问题;

Context:

我们增加Provider.js,代码如下:

import {PropTypes, Component} from 'react';

class Provider extends Component {

  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children; //Provider包裹的子元素输出出来
  }
}

Provider.contextTypes = {
  store: PropTypes.object
}
export default Provider;

index.js 文件引入Provider


import React from 'react';
import ReactDOM from 'react-dom';
import ControlPanel from './views/ControlPanel';
import store from './Store.js';
import Provider from './Provider.js';
ReactDOM.render(
  <Provider store={store}>
    <ControlPanel />
  </Provider>,
  document.getElementById('root')
);

最后我们在修改ControlPanel中的Counter组件,


1.jpg

2.jpg

3.jpg

4.jpg
React-Redux:

如果理解上面的例子之后你会发现有些复用的部分可以提取出来,各个组件关心自己的部分就行了,react-redux库就是解决这个事情的,让你开发爽到飞起

react-redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它。

首先我们先了解一下重要的函数connect,React-Redux 提供connect方法,用于从 UI 组件生成容器组件,就是将这两种组件连起来。
connect方法的完整 API

import { connect } from 'react-redux'
const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

connect方法接受两个参数:mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。

此时index.js文件变成:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';

import ControlPanel from './views/ControlPanel';
import store from './Store.js';
ReactDOM.render(
  <Provider store={store}>
    <ControlPanel/>
  </Provider>,
  document.getElementById('root')
);
//Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了

Counter.js文件变成

import React, { PropTypes } from 'react';
import * as Actions from '../Actions.js';
import {connect} from 'react-redux';

const buttonStyle = {
  margin: '10px'
};

function Counter({caption, onIncrement, onDecrement, value}) {
  return (
    <div>
      <button style={buttonStyle} onClick={onIncrement}>+</button>
      <button style={buttonStyle} onClick={onDecrement}>-</button>
      <span>{caption} count: {value}</span>
    </div>
  );
}

Counter.propTypes = {
  caption: PropTypes.string.isRequired,
  onIncrement: PropTypes.func.isRequired,
  onDecrement: PropTypes.func.isRequired,
  value: PropTypes.number.isRequired
};

function mapStateToProps(state, ownProps) {
  return {
    value: state[ownProps.caption]
  }
}

function mapDispatchToProps(dispatch, ownProps) {
  return {
    onIncrement: () => {
      dispatch(Actions.increment(ownProps.caption));
    },
    onDecrement: () => {
      dispatch(Actions.decrement(ownProps.caption));
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

connect函数实际上是个高阶函数,了解可以参考这边文章
Higher-Order Components

关于react 路由可以参考这边文章
路由

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容