ReactJS学习系列课程React 整体流程使用案例

对于一门语言的学习,少不了动手练习,今天我们就尝试一下,如何用React编写一个简单的程序,实现功能包括网络请求数据,绑定数据进行增删改查,并进行相应的路由操作。

下面我们来年代码:

package.json

我们创建一个package.json,里面包含一些开发库还有核心库:

{
  "name": "demo4","version": "1.0.0","description": "","scripts": { "start": "webpack-dev-server --progress --colors --hot --inline -d","build": "webpack --progress --colors --minify" },"license": "ISC","dependencies": { "classnames": "2.1.2","react": "0.13.3","react-router": "^2.5.1" },"devDependencies": { "babel-core": "5.6.18","babel-eslint": "^5.0.4","babel-loader": "5.1.4","node-args": "1.0.2","node-libs-browser": "^1.0.0","raw-loader": "0.5.1","eslint": "^1.10.3","eslint-config-rackt": "^1.1.1","eslint-plugin-react": "^3.16.1","style-loader": "0.12.3","todomvc-app-css": "2.0.1","webpack": "1.9.11","webpack-dev-server": "1.11.0" } }

react 和react-router是我们一定要添加的核心库,react-router是路由功能的核心库,如果我们要进行页面跳转,一定要用到。

还有一些开发库,比如webpack, 用于打包工作,babel用于我们要把ES6代码转化,webpack-dev-server主要负责本地测试服务器功能,可以把我们的测试部署到上面,配置hot-reload进行实时刷新工作。

对于这个package.json,我们配置好以后,可以执行npm install进行全部安装。

webpack.config.js

然后我们在看一下webpack.config.js的编写:

var path = require('path');
var webpack = require('webpack');

var env = process.env.NODE_ENV;

var config = {
  entry: ['./app'],output: {
    path: path.join(__dirname,'dist'),publicPath: '/dist/',filename: 'bundle.js'
  },plugins: [],module: {
    loaders: [{
      test: /\.js$/,exclude: /node_modules/,include: __dirname,loader: 'babel'
    },{
      test: /\.css?$/,loaders: ['style','raw'],include: __dirname
    }]
  }
};


  config.plugins = config.plugins.concat(
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),new webpack.optimize.OccurenceOrderPlugin());


module.exports = config;

其中主要的功能是entry 和output,我们要定义个程序的如果,方便webpack进行代码打包工作,这个我们的如果是app.js, 在制定一个输入路径,这里我们引用了node的path库,以便于指定当前文件路径。

在配置一个loader,进行代码转化工作:

loaders: [{
      test: /\.js$/,exclude: /node_modules/,include: __dirname,loader: 'babel'
    },

ConfigureStore.js

我们接下来创建一个Store用于数据管理, 比如从网络获取数据,用户交互过程中,数据更换等等操作, 我们看一下代码:

const API = 'http://addressbook-api.herokuapp.com/contacts';

let _contacts = []
let _initCalled = false
let _changeListeners = []

const ContactStore = {
  init: function () {
    if (_initCalled)
      return

    _initCalled = true
    getJSON(API,(err,res) => { res.contacts.forEach(contact => { _contacts[contact.id] = contact; }) ContactStore.notifyChange(); }) },addContact: function (contact,cb) { postJSON(API,{ contact: contact},res => { _contacts[res.contact.id] = res.contact ContactStore.notifyChange() if (cb) cb(res.contact) }) },removeContact: function (id,cb) { deleteJSON(API + '/' +id,cb) delete _contacts[id] ContactStore.notifyChange() },getContacts: function () { const array = [] for (const id in _contacts) array.push(_contacts[id]) return array; },getContact: function(id) { return _contacts[id] },notifyChange: function() { _changeListeners.forEach(listener => { listener() }) },addChangeListener: function (listener) { _changeListeners.push(listener) },removeChangeListener: function(listener) { _changeListeners = _changeListeners.filter(l => { return listener !== l }) } } localStorage.token = localStorage.token || (Date.now() * Math.random()) function getJSON(url,cb) { const req = new XMLHttpRequest() req.onload = function () { if (req.status === 404) { cb(new Error('not found')) } else { cb(null,JSON.parse(req.response)) } } req.open('GET',url) req.setRequestHeader('authorization',localStorage.token) req.send() } function postJSON(url,obj,cb) { const req = new XMLHttpRequest() req.onload = () => {
    cb(JSON.parse(req.response))
  }
  req.open('POST',url)
  req.setRequestHeader('Content-Type','application/json;charset=UTF-8')
  req.setRequestHeader('authorization',localStorage.token)
  req.send(JSON.stringify(obj))
}

function deleteJSON(url,cb) {
  const req = new XMLHttpRequest()
  req.onload = cb
  req.open('DELETE',url)
  req.setRequestHeader('authorization',localStorage.token)
  req.send()
}

export default ContactStore

以上语法用ES6编写,主要实现功能是对数据进行增删改查的操作,并把数据提交到后台服务器。数据请求用到XMLHttpRequest,是javascript最原始的ajax方案,但是对于es6做了很大改进,个人很喜欢。

app.js

最后我们编写一个主入口文件app.js, 我们要在这个文件中创建几个组件比如App,整个应用的root组件,还有contact组件,用于显示联系人信息, 还有newcontact组件,用于创建一个联系人, 空组件,用于显示无信息状态,最后是一个首页显示组件。

在这个文件中,我们还要创建一个router,用来处理页面的跳转操作。

我们看一下代码:

import React from 'react';
import {
  browserHistory,Router,Route,IndexRoute,Link,withRouter
} from 'react-router';
import ContactStore from './ContactStore';
import './app.css';

const App = React.createClass({
  getInitialState() {
    return {
      contacts: ContactStore.getContacts(),loading: true
    };
  },componentWillMount() {
    ContactStore.init();
  },componentDidMount() {
    ContactStore.addChangeListener(this.updateContacts);
  },componentWillUnmount() {
    ContactStore.removeChangeListener(this.updateContacts);
  },updateContacts() {
    if(!this.isMounted())
      return

      this.setState({
        contacts: ContactStore.getContacts(),loading: false
      })
  },render() {
    const contacts = this.state.contacts.map(contact => {
      return <li key={contact.id}><Link to={`/contact/${contact.id}`}>{contact.first}</Link></li>;
    });

    return (
      <div className="App">
        <div className="ContactList">
            <Link to="/contact/new">New Contact</Link>
          <ul>
            {contacts}
          </ul>
        </div>
        <div className="Content">
          {this.props.children}
        </div>
      </div>
    );
  }
});

const Index = React.createClass({
   render(){
       return <h1>Address Book</h1>
   }
});

const Contact = withRouter(
  React.createClass({
    getStateFromStore(props){
      const {id} = props? props.params: this.props.params
      return {
        contact: ContactStore.getContact(id)
      };
    },getInitialState() {
      return this.getStateFromStore();
    },componnentDidMount() {
      ContactStore.addChangeListener(this.updateContact);
    },componentWillUnmount() {
      ContactStore.removeChangeListener(this.updateContact);
    },componentWillReceiveProps(nextProps) {
      this.setState(this.getStateFromStore(nextProps));
    },updateContact() {
      if(!this.isMounted) {
        return;
      }
      this.setState(this.getStateFromStore);
    },destory() {
      const {id} = this.props.params;
      ContactStore.removeContact(id);
      this.props.router.push('/');
    },render() {
      const contact = this.state.contact || {};
      const name = contact.first + '' + contact.last;
      const avatar = contact.avatar || 'http://placecage.com/50/50';

      return (
        <div className="Contact">
          <img height="50" src={avatar} key={avatar} />
          <h3>{name}</h3>
          <button onClick={this.destory}>Delete</button>
        </div>
      );
    }
  })
);

const NewContact = withRouter(
  React.createClass({
    createContact(event) {
      event.preventDefault();
      ContactStore.addContact({
        first: React.findDOMNode(this.refs.first).value,last: React.findDOMNode(this.refs.last).value
      },(contact) => {

        this.props.router.push(`/contact/${contact.id}`);
      });
    },render() {
      return (
        <form onSubmit={this.createContact}>
          <p>
            <input type="text" ref="first" placeholder="First name"/>
            <input type="text" ref="last" placeholder="Last name"/>
          </p>
          <p>
            <button type="submit">Save</button> <Link to="/">Cancel</Link>
          </p>
        </form>
      );
    }
  })
);

const NotFound = React.createClass({
  render(){
    return <h2>Not Found</h2>
  },})


React.render((
  <Router history={browserHistory}>
    <Route path="/" component={App}>
      <IndexRoute component={Index} />
      <Route path="contact/new" component={NewContact} />
      <Route path="contact/:id" component={Contact} />
      <Route path="*" component={NotFound} />
    </Route>
  </Router>
),document.getElementById('example'))

对于这个Demo,没有做更详细的解释,相应大家这个阶段,对react已经有了初步了解,如果还是不理解,可以参考之前的教程。

代码地址:https://github.com/jiangbophd/React-Learning

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom