react 项目实战(二)创建 用户添加 页面 及 fetch请求 json-server db.json -w -p 8000
1.安装 路由
npm install -S react-router@3.x
2.新增页面
我们现在的应用只有一个Hello React的页面,现在需要添加一个用于添加用户的页面。
首先在/src
目录下新增一个pages
目录,用于存放渲染页面的组件。
然后在/src/pages
中新增一个UserAdd.js
文件。
在这个文件中写入一个基本的React组件:
import React from 'react'; // 添加用户组件 class UserAdd extends React.Component { // 渲染 render() { return ( <div>User add page.</div> ); } } // 向外暴露 export default UserAdd;
3.配置路由
我们需要使用react-router提供的路由组件来控制当前路由下页面应该渲染的组件。
修改/src/index.js
为:
// 配置路由 import React from 'react'; import ReactDOM from 'react-dom'; // 引入react-router import { Router, Route, hashHistory } from 'react-router'; import UserAddPage from './pages/UserAdd' // 渲染 ReactDOM.render(( <Router history={hashHistory}> <Route path="/user/add" component={UserAddPage} /> </Router> ), document.getElementById('root'));
打开浏览器的http://localhost:3000
发现页面一片空白,在控制台中可以看到一个错误信息:
这是因为我们只配了一个/user/add的路由,我们来访问一下http://localhost:3000/#/user/add
看看
新页面出来了。现在需要把上面的那个错误干掉:新建一个主页,并在主页中添加一个链接链到添加用户的页面。
还是在/src/pages/
中新建一个Home.js
,写入代码:
import React from 'react'; import { Link } from 'react-router'; class Home extends React.Component { render () { return ( <div> <header> <h1>Welcome</h1> </header> <main> <Link to="/user/add">添加用户</Link> </main> </div> ); } } export default Home;
然后在/src/index.js
中添加一个指向Home的Route:
// 配置路由 import React from 'react'; import ReactDOM from 'react-dom'; // 引入react-router import { Router, Route, hashHistory } from 'react-router'; import HomePage from './pages/Home'; import UserAddPage from './pages/UserAdd'; // 渲染 ReactDOM.render(( <Router history={hashHistory}> <Route path="/" component={HomePage} /> <Route path="/user/add" component={UserAddPage} /> </Router> ), document.getElementById('root'));
首页已经可以正常访问了,并且点击添加用户也正确地跳转到了添加用户页面。
Q: 为什么我看到的url里有一个’#’?
A: 这是由于我们给Router组件传入了hashHistory,url中’#’及’#’以后的部分属于hash,hash的变化并不会引起页面的重新刷新,而hashHistory会监听hash的变化使得Router组件能够根据url渲染出正确的组件。除了hash History之外还有browserHistory和memoryHistory。使用browserHistory可以让url变得像标准的url一样(没有#),但是需要在后端做一些特殊处理;memoryHistory是用于做服务端渲染时使用的。
Q: 为什么Home.js里要用Link组件而不是一个标准的a标签?
A: 上面说了,我们使用了hashHistory,正确的页面url中应该都是有一个’#’的,如果直接使用a标签,你需要这么写:<a href=”/#/user/add”>添加用户</a>。但是如果我们想要换成browserHistory,就需要把所有标签中的’#’去掉。使用react-router提供的Link组件可以让我们无视history之间的差异性,直接写标准的路由”/user/add”就可以了。此外,由于我们写的是单页面应用(SPA),Link组件会阻止页面的跳转(仅仅只是改变了url,然后改变了渲染的组件)。
4.编写页面
我们定义了User的结构为:
{ "id": 10000, "name": "一韬", "age": 25, "gender": "male" }
在新建的时候,我们需要使用post方式将新用户的name、age、gender发送给接口http://localhost:3000/user
,因此这个添加用户的页面需要提供一个包含上述3个字段控件的表单:
src / pages / UserAdd.js
import React from 'react'; // 添加用户组件 class UserAdd extends React.Component { // 构造器 constructor(props) { super(props); // 定义初始化状态 this.state = {}; } render() { return ( <div> <header> <div>添加用户</div> </header> <main> <form> <label>用户名:</label> <input type="text" /> <br /> <label>年龄:</label> <input type="number" /> <br /> <label>性别:</label> <select> <option value="">请选择</option> <option value="male">男</option> <option value="female">女</option> </select> <br /> <br /> <input type="submit" value="提交" /> </form> </main> </div> ); } } export default UserAdd;
现在我们的页面是这个样子:
获取表单的值
现在我们可以在表单中填写数据了,但是还不能获取到我们输入的值。
在React中处理表单有些不一样,由于React提倡“单向数据流”,React中的表单并不提供双向数据绑定的功能,我们需要给表单绑定它的value,然后提供一个onChange的处理方法来更新value的值。
这里我们使用组件的state来维护表单的值,在onChange的时候使用setState来更新值,最后,在表单提交事件被触发的时候,我们输出state来观察最终获得的表单值:
src / pages / UserAdd.js
import React from 'react'; // 添加用户组件 class UserAdd extends React.Component { // 构造器 constructor(props) { super(props); // 定义初始化状态 this.state = { name: '', age: 0, gender: '' }; } // 输入框改变事件 handleValueChange(field, value, type='string') { // 由于表单的值都是字符串,我们可以根据传入type为number来手动转换value的类型为number类型 if(type === 'number'){ value = + value; } // 设置状态值 this.setState({ [field]: value }); } // 按钮提交事件 handleSubmit(e){ // 阻止表单submit事件自动跳转页面的动作 e.preventDefault(); alert(JSON.stringify(this.state)); } render() { // 定义常量 const {name, age, gender} = this.state; return ( <div> <header> <div>添加用户</div> </header> <main> <form onSubmit={(e) => this.handleSubmit(e)}> <label>用户名:</label> <input type="text" value={name} onChange={(e) => this.handleValueChange('name', e.target.value)} /> <br /> <label>年龄:</label> <input type="number" value={age || ''} onChange={(e) => this.handleValueChange('age', e.target.value, 'number')} /> <br /> <label>性别:</label> <select value={gender} onChange={(e) => this.handleValueChange('gender', e.target.value)}> <option value="">请选择</option> <option value="male">男</option> <option value="female">女</option> </select> <br /> <br /> <input type="submit" value="提交" /> </form> </main> </div> ); } } export default UserAdd;
在页面中填写表单,点提交后可以看到如下界面:
调用接口创建用户
拿到了表单数据,我们离目标已经很近了,就差最后一步:把表单的值通过接口提交
调用接口可以有很多方法:Ajax、表单提交、fetch。
由于直接使用表单提交会引起页面的跳转,这不符合我们单页应用的原则。这里我们使用比Ajax更先进易用的fetch。
修改/pages/UserAdd.js
中的handleSubmit
方法:
handleSubmit (e) { e.preventDefault(); const {name, age, gender} = this.state; fetch('http://localhost:3000/user', { method: 'post', // 使用fetch提交的json数据需要使用JSON.stringify转换为字符串 body: JSON.stringify({ name, age, gender }), headers: { 'Content-Type': 'application/json' } }) .then((res) => res.json()) .then((res) => { // 当添加成功时,返回的json对象中应包含一个有效的id字段 // 所以可以使用res.id来判断添加是否成功 if (res.id) { alert('添加用户成功'); this.setState({ name: '', age: 0, gender: '' }); } else { alert('添加失败'); } }) .catch((err) => console.error(err)); }
填写表单然后点击提交后,可以看到弹出一个添加成功的提示框:
在/server
目录执行json-server db.json -w -p 8000
打开http://localhost:8000/user
查看所有用户列表,发现用户已经被添加进去了:
至此就完成了基本的添加用户功能~
注:完成的代码 src / pages / UserAdd.js
import React from 'react'; // 添加用户组件 class UserAdd extends React.Component { // 构造器 constructor(props) { super(props); // 定义初始化状态 this.state = { name: '', age: 0, gender: '' }; } // 输入框改变事件 handleValueChange(field, value, type='string') { // 由于表单的值都是字符串,我们可以根据传入type为number来手动转换value的类型为number类型 if(type === 'number'){ value = + value; } // 设置状态值 this.setState({ [field]: value }); } // 按钮提交事件 handleSubmit(e){ // 阻止表单submit事件自动跳转页面的动作 e.preventDefault(); // 定义常量 const { name, age, gender } = this.state; // 发送请求 fetch('http://localhost:8000/user', { method: 'post', // 使用fetch提交的json数据需要使用JSON.stringify转换为字符串 body: JSON.stringify({ name, age, gender }), headers: { 'Content-Type': 'application/json' } }) // 强制回调的数据格式为json .then((res) => res.json()) // 成功的回调 .then((res) => { // 当添加成功时,返回的json对象中应包含一个有效的id字段 // 所以可以使用res.id来判断添加是否成功 if(res.id){ alert('添加用户成功!'); // 初始化 this.setState({ name: '', age: 0, gender: '' }); }else{ alert('添加用户失败!'); } }) // 失败的回调 .catch((err) => console.error(err)); } render() { // 定义常量 const {name, age, gender} = this.state; return ( <div> <header> <div>添加用户</div> </header> <main> <form onSubmit={(e) => this.handleSubmit(e)}> <label>用户名:</label> <input type="text" value={name} onChange={(e) => this.handleValueChange('name', e.target.value)} /> <br /> <label>年龄:</label> <input type="number" value={age || ''} onChange={(e) => this.handleValueChange('age', e.target.value, 'number')} /> <br /> <label>性别:</label> <select value={gender} onChange={(e) => this.handleValueChange('gender', e.target.value)}> <option value="">请选择</option> <option value="male">男</option> <option value="female">女</option> </select> <br /> <br /> <input type="submit" value="提交" /> </form> </main> </div> ); } } export default UserAdd;
.