React项目实战:react-redux-router基本原理

时间: 2017-11-27阅读: 695标签: react作者: cinglong

JSX

本质上来讲,JSX 只是为React.createElement(component, props, ...children)方法提供的语法糖。比如下面的代码:

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

编译为:

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React.createElement()这个方法首先会进行一些避免bug的检查,之后会返回一个类似下面例子的对象:

const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};

这样的对象被称为React元素,它代表所有你在屏幕上看到的东西。
我们用 React 开发应用时一般只会定义一个根节点。要将 React 元素渲染到根DOM节点中,我们通过把它们都传递给ReactDOM.render()的方法来将其渲染到页面上:

ReactDOM.render(
  element,
  document.getElementById('root')
);

每当 React 元素发生变化时,ReactDOM首先会比较元素内容先后的不同,然后操作浏览器DOM更新改变了的部分。

组件 & Props

当 React 遇到的元素是用户自定义的组件,它会将 JSX 属性作为单个对象传递给该组件,这个对象称之为props。无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props 。
例如,这段代码会在页面上渲染出Hello,Sara:

//使用 ES6 class 来定义一个组件,组件名称必须以大写字母开头。
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

我们来回顾一下在这个例子中发生了什么:

    我们对<Welcome name="Sara" />元素调用了ReactDOM.render()方法。

    React 将{name: 'Sara'}作为props传入并调用 Welcome 组件。

    Welcome 组件将<h1>Hello, Sara</h1>元素作为结果返回。

    ReactDOM 将DOM更新为<h1>Hello, Sara</h1>。

State & 生命周期

组件的通过props获取属性,且其不能修改;当我们需要修改当前组件的状态时,要用到state来设置局部状态,需要通过this.setState()来更新组件局部状态:

class Toggle extends React.Component {
  constructor(props) {
    super(props);    //初始化this,并赋值this.props
    this.state = {isToggleOn: true};    //初始化this.state
    this.handleClick = this.handleClick.bind(this);    //为this.handleClick绑定this对象
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));    //用this.setState()更新this.state
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

每一个组件都有几个你可以重写以让代码在处理环节的特定时期运行的“生命周期方法”。方法中带有前缀will的在特定环节之前被调用,而带有前缀did的方法则会在特定环节之后被调用。

装配:这些方法会在组件实例被创建和插入DOM中时被调用:

- constructor(`props`)
- componentWillMount()
- render()
- componentDidMount()

更新:属性或状态的改变会触发一次更新。当一个组件在被重渲时,这些方法将会被调用:

- componentWillReceiveProps(`nextProps`)
- shouldComponentUpdate(`nextProps`, `nextState`)
- componentWillUpdate(`nextProps`, `nextState`)
- render()
- componentDidUpdate(`prevProps`, `prevState`)

卸载:当一个组件被从DOM中移除时,该方法被调用:

- componentWillUnmount()

当项目视图交互复杂且频繁的时候,依旧采用 state 进行状态更改会显得异常繁琐和不可预测。
这时我们就需要借助 Redux 框架,将状态数据全部转交给 Redux 处理,React 专一负责视图显示,这样会让项目逻辑变得简单而清晰。


Redux相关

三大原则:

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个store中。

惟一改变 state 的方法就是触发action,action 是一个用于描述事件的普通对象。

为了描述 action 如何改变 state tree ,你需要编写reducers。

Action

Action 是把数据从项目传到 store 的有效载荷。它是 store 数据的唯一来源。通常你会通过store.dispatch()将 action 传到 store。

Action 本质上是 JavaScript 普通对象,添加新 todo 任务的 action 是这样的:

{
  type: 'ADD_TODO',
  text: 'Build my first Redux app'
}

Action 创建函数就是生成 action 的方法。在 Redux 中的 action 创建函数只是简单的返回一个 action:

function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text: text
  }
}

这样做将使 action 创建函数更容易被移植和测试。只需把 action 创建函数的结果传给 dispatch() 方法即可发起一次 dispatch 过程。

dispatch(addTodo(text));

//或者创建一个 被绑定的 action 创建函数 来自动 dispatch:
const boundAddTodo = (text) => dispatch(addTodo(text));
boundAddTodo(text);

store 里能直接通过 store.dispatch() 调用 dispatch() 方法,但是多数情况下你会使用 react-redux 提供的connect()帮助器来调用。

Reducer

Action 只是描述了有事情发生了这一事实,而reducer要做的事情正是指明应用如何更新 state 。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。

(previousState, action) => newState

保持 reducer 纯净非常重要。永远不要在 reducer 里做这些操作:

修改传入参数;

执行有副作用的操作,如 API 请求和路由跳转;

调用非纯函数,如 Date.now() 或 Math.random()。

我们将以指定 state 的初始状态作为开始。Redux 首次执行时,state 为 undefined,此时我们可借机设置并返回应用的初始 state:

const initialState = {};    //初始化state

function todoApp(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return Object.assign({}, state, {
        text: action.text
      })
    default:
      return state    //在 default 情况下返回旧的 state
  }
}

每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同,分别对应它管理的那部分 state 数据。

combineReducers()所做的只是生成一个函数,这个函数来调用你的一系列 reducer,每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。

import { combineReducers } from 'redux';

const todoApp = combineReducers({
  visibilityFilter,
  todos
})

export default todoApp;

注意上面的写法和下面完全等价:

export default function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter, action),
    todos: todos(state.todos, action)
  }
}

combineReducers 接收一个对象,可以把所有顶级的 reducer 放到一个独立的文件中,通过 export 暴露出每个 reducer 函数,然后使用 import * as reducers 得到一个以它们名字作为 key 的 object:

import { combineReducers } from 'redux'
import * as reducers from './reducers'

const todoApp = combineReducers(reducers)

Store

action 来描述“发生了什么,reducers 根据 action 更新 state,Store就是把它们联系到一起的对象。Store 有以下职责:

维持应用的 state;

提供getState()方法获取 state;

提供dispatch(action)方法更新state;

通过subscribe(listener)注册监听器;

通过subscribe(listener)返回的函数注销监听器。

我们使用 combineReducers() 将多个 reducer 合并成为一个。现在我们将其导入,并传递 createStore()。

import { createStore } from 'redux'
import todoApp from './reducers'
let store = createStore(todoApp)

createStore() 的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。

let store = createStore(todoApp, window.STATE_FROM_SERVER);


数据流

Redux 应用中数据的生命周期遵循下面 4 个步骤:

    调用 store.dispatch(action)。

    Redux store 调用传入的 reducer 函数。

    根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。

    Redux store 保存了根 reducer 返回的完整 state 树。

Router相关

直接使用整合后的react-router-redux,后面抽时间再详细讲一下,具体使用的话模仿官方案例吧,官方文档

容器组件 和 展示组件

Redux 的 React 绑定库包含了 容器组件和展示组件相分离 的开发思想。

明智的做法是只在最顶层组件(如路由操作)里使用 Redux。其余内部组件仅仅是展示性的,所有数据都通过 props 传入。

原文来源:https://segmentfault.com/a/1190000012170435


React 中同构(SSR)原理脉络梳理

随着越来越多新型前端框架的推出,SSR 这个概念在前端开发领域的流行度越来越高,也有越来越多的项目采用这种技术方案进行了实现。SSR 产生的背景是什么?适用的场景是什么?实现的原理又是什么?

自定义组件v-model的实质性理解

在React中如果想实现类似于v-model的功能,一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的

解读React的pooledClass.js_对象池技术的原理/思路

单例模式是限制了一个类只能有一个实例,对象池模式则是限制一个类实例的个数。对象池技术基本原理的核心有两点:缓存和共享,即对于那些被频繁使用的对象,在使用完后,不立即将它们释放,而是将它们缓存起来

React router动态加载组件-适配器模式的应用

本文讲述怎么实现动态加载组件,并借此阐述适配器模式,自定义高阶组件的好处,是可以按最少的改动,来优化已有的旧项目。只需要改变import组件的方式即可。花最少的代价,就可以得到页面性能的提升。

React中的处理事件_React如何处理事件

主要说一下React是如何处理事件的。事件的处理是前端开发过程中非常重要的一部分,通过事件处理,我们可以响应用户的各种操作,从而实现一个富交互的应用。

Redux与它的中间件:redux-thunk,redux-actions,redux-promise,redux-sage

这里要讲的就是一个Redux在React中的应用问题,讲一讲Redux,react-redux,redux-thunk,redux-actions,redux-promise,redux-sage这些包的作用和他们解决的问题。

React事件处理函数必须使用bind(this)的原因

学习React的过程中发现调用函数的时候必须使用bind(this),之后直接在class中声明函数即可正常使用,但是为什么呢,博主进行了一番查阅,总结如下。

React 新 Context API

React 新 Context API它更符合工程化, 不再是实验性的,现在它是一流的API! 并且它还使用了 RENDER PROP!你在react官网上听说过 context API?那么你为何要使用context?Context的重生

深入 React 高阶组件

本文面向想要探索 HOC 模式的进阶用户,如果你是 React 的初学者则应该从官方文档开始。高阶组件(Higher Order Components)是一种很棒的模式,已被很多 React 库证实是非常有价值的。

ReactJS Components: 基础指南

创建和管理React组件的各种方式,涌现的大量状态管理工具等等都是这些挑战的焦点。我们今天能做的就是在React(基于社区选择)中将最常用的做法引入桌面并讨论它们。