项目结构

  上一章我们创建完了一个React项目,现在打开我们的项目文件夹,我们能看到node_modules,public,和src三个子文件夹以及package.jsonREADME等文件。注意src这个文件夹,我们之后要把重心放在src这个文件夹里。


我们开始做一个Todo项目吧

第一步:打开public文件夹下的index.html

  我们可以看到html的文件结构,熟悉html的同学应该很容易能看懂了,这是一个再简单不过的结构了,我们把文件内容替换成以下内容,当然你也可以起一个你喜欢的名字,加一张你喜欢的图标,不过,我们一切从简,注意body里的id,这很重要。

<!DOCTYPE html>
<html>
    <title>React Todo</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

第二步:打开src下的App.js文件

  这个文件就是我们的主页所在的文件了,但是为什么不放在index.js里呢?按我的个人理解,这就是一个良好的代码风格,而且之后还会涉及到页面路由等,因为你的网页不可能只是一个主页,还会有许多的子页面,把这些子页面单独放在一个文件里,还可以作为模板重复利用,而且在index.js里我们可以做一些额外的配置,比如使用中间件等,这样模块化开发会让代码可读性更强。(我随便说的,大佬请指正!)好了,废话不多说,我们先把代码放出来。

import React, { Component } from "react";

class App extends Component {
  render() {
    return (
        <div className="todo">
          <div className="header">
            <form>
              <input placeholder="输入任务">
              </input>
              <button type="submit">添加</button>
            </form>
          </div>
        </div>
    );
  }
}

export default App;

  点击保存,然后刷新我们的网页看看(一般来说保存了就会自动刷新,记得先npm start一下)

第三步:添加功能

  现在就算你输入了一个任务,你也点了添加了,但是好像并没有发生什么事情。当然了,你都还没添加功能进去呢,难道凭空给你生出来?
先别急,我们想想我们做一个todo项目需要什么功能?

  • 添加任务
  • 完成任务
  • 删除任务
  • 修改任务

我们先来实现添加任务

  我们在render()前插入以下代码,我们定义一个addItem函数

constructor(props) {
   super(props);
   this.addItem = this.addItem.bind(this);
 }
 
 addItem(e) {

 }

  记得我们在render()里插入了一个form标签吗?我们需要在form提交的时候触发addItem函数,我们在form标签里加入一个onSubmit={this.addItem},像这样:

<form onSubmit={this.addItem}>

  但是addItem函数的参数怎么来呢?那么,我们就要想办法获取input输入框的内容。有两种方法:

  • 非受控组件(即ref)
  • 受控组件(即onChange或onClick等方法)

  我们暂时采用第一种方法,第二种方法大家可以自行尝试,现在代码变成了这样:

<input ref={(a) => this._inputElement = a }
       placeholder="输入任务">

  我们来验证一下这样是否可行,我们在addItem函数里让输入框的值在控制台显示一下。

addItem(e) {
		console.log(this._inputElement.value)
    }

  但是这个时候我们会发现浏览器好像会自动刷新,然后控制台的内容一下就没了,那怎么办呢?我们加一行代码:

addItem(e) {
		console.log(this._inputElement.value)
		e.preventDefault();
    }

  这能阻止浏览器的默认行为,这里就是不让它刷新了
  好了,我们已经成功获取到输入框的值了,接下来,我们要怎么把它存起来?没错,我们用state来储存我们的值。
  我们在super(props);后面初始化一下state,并在里面放一个数组类型的items

...
constructor(props) {
        super(props);
		this.state = {
    	items: []
  		};
  ...

  然后我们在addItem函数里,将输入框的值追加给这个items数组

addItem(e) {
	if (this._inputElement.value !== "") {
    var newItem = {
      text: this._inputElement.value,
      key: Date.now()
    };
 
    this.setState((prevState) => {
      return { 
        items: prevState.items.concat(newItem) 
      };
    });
   
    this._inputElement.value = "";
  }
   
  console.log(this.state.items);
  e.preventDefault();
}

  但是我们发现好像控制台显示的有些问题,它并没有将我们刚刚添加进去的值显示出来,而是显示的前面添加过的值,这是因为React的state状态更新机制引起的,我们已经实现了添加功能了,开心!下面是完整代码。

import React, { Component } from "react";

class App extends Component {
    constructor(props) {
        super(props);
		this.state = {
    	items: []
  		};
        this.addItem = this.addItem.bind(this);
    }

    addItem(e) {
	if (this._inputElement.value !== "") {
    var newItem = {
      text: this._inputElement.value,
      key: Date.now()
    };
 
    this.setState((prevState) => {
      return { 
        items: prevState.items.concat(newItem) 
      };
    });
    
    this._inputElement.value = "";
  	console.log(this.state.items);
  	e.preventDefault();
	}
  render() {
    return (
        <div className="todo">
          <div className="header">
            <form onSubmit={this.addItem}>
              <input ref={(a) => this._inputElement = a } 
              placeholder="输入任务">
              </input>
              <button type="submit">添加</button>
            </form>
          </div>
        </div>
    );
  }
}

export default App;

总结

  今天先到这,我们已经能实现了获取表单值并把它添加到state里了,下一章我们将会做更多的事,大家可以先想想应该怎么把数组里的值显示出来,然后该怎么去修改或删除它!