前言
1、故事背景
Build App with Reactcss
Project系列,主要是从实践的角度来分析需求,设计组件,设计网页。html

2、组件设计纲要
/* implement */react
代码分析
1、项目配置问题
.eslintrc文件采用airbnb的严格检查模式。json

package.json中添加:redux


快捷键直接获得以下代码模板:api


点击后出现颜色值提取器,以下:react-router

填下以下,这里使用了 semanitic-ui。app
import React from "react";
import PropTypes from "prop-types";
const InlineError = ({ text }) => (
<span style={{ color: "#ae5856" }}>{text}</span>
);
# 参数检查
InlineError.propTypes = {
text: PropTypes.string.isRequired
};
export default InlineError;
2、组件设计与实现
(1) This requires “yarn add react-router-dom”dom
[index.js] ide

(2) 配置主页 HomePage
a. 打算 导入HomePage.js,并做为路由根路径。path = "/" exact
b. 下一步,就是实现HomePage,至少包括了login的连接按钮。
[HomePage.js]

(3) 实现主页 HomePage,以及其中的 Link。
[HomePage.js]

(4) 实现 LoginPage
这里主要是添加上对应的路由。
[App.js]

以上便实现了一个基本的路由过程。
为何要用exact?
以下状况下,若是匹配路由path='/page',那么会把Home也会展现出来,这就是为何要使用exact。
<Route path='/' component={Home} />
<Route path='/page' component={Page}>
(1) semanitic-ui
Ref: https://react.semantic-ui.com/usage
安装并导入:
$ yarn add semantic-ui-react
$ yarn add semantic-ui-css
import 'semantic-ui-css/semantic.min.css';
className的值创建在semanitic-ui上,以后须要系统学习。

(2) LoginForm
render 的内容,首先得到须要的 value:
const { data, errors, loading } = this.state;
而后渲染。
<Form.Field ={!!errors.email}>
<label htmlFor="email">Email</label>
<input
type ="email"
id ="email"
name ="email"
placeholder="example@example.com"
value ={data.email}
onChange ={this.onChange}
/>
{errors.email && <InlineError text={errors.email} />} # 判断“有错误”才出现提示
</Form.Field>
<Form.Field error={!!errors.password}>
<label htmlFor="password">Password</label>
<input
type ="password"
id ="password"
name ="password"
placeholder="Make it secure"
value ={data.password}
onChange ={this.onChange}
/>
{errors.password && <InlineError text={errors.password} />}
</Form.Field>
格式错误后出现小提示。

(3) onChange
onChange = e => {
console.log("-->name " + e.target.name) console.log("-->value " + e.target.value)
this.setState({
data: { ...this.state.data, [e.target.name]: e.target.value }
});
}
a) 若是去掉...this.state.data会报警告以下:

b) 由于我是须要先总体覆盖一次,也就是setState的第一个参数,而后再改变咱们想要改变的。
(4) onSubmit
首先要验证下格式;而后再提交登陆。
validate = data => {
const errors = {};
if (!Validator.isEmail(data.email)) errors.email = "Invalid email";
if (!data.password) errors.password = "Can't be blank";
return errors;
};
提交操做的实现,以下:
[LoginForm.js]
onSubmit = () => {
const errors = this.validate(this.state.data);
this.setState({ errors });
if (Object.keys(errors).length === 0) { // 格式没问题的话
this.setState({ loading: true });
this.props.submit(this.state.data)
.catch (err =>
this.setState({ errors: err.response.data.errors, loading: false })
);
}
};
submit 真正的实现之地在外层。
[LoginPage.js]
class LoginPage extends React.Component {
submit = data =>
this.props.login(data).then(() => this.props.history.push("/dashboard"));
/* 以后会调用到 ./actions/auth.js中的login */
render() {
return (
<div>
<h1>Login page</h1>
<LoginForm submit={this.submit} />
<Link to="/forgot_password">Forgot Password?</Link>
</div>
);
}
}
(5) error={!!errors.email}
双重否认表示的意义是什么? Ref: js双感叹号判断
至关于三元运算符,返回boolean值。
var ret = !!document.getElementById
等价于:
var ret = document.getElementById ? true : false;
当值是非空字符串和非零数字返回true,当值是空字符串、0或者null返回false。
var a = " "; alert(!!a); //true
var a = "s"; alert(!!a); //true
var a = true; alert(!!a); //true
var a = 1; alert(!!a); //true
var a = -1; alert(!!a); //true
var a = -2; alert(!!a); //true
var a = 0; alert(!!a); //false
var a = ""; alert(!!a); //false
var a = false; alert(!!a); //false
var a = null; alert(!!a); //false
3、Redux的应用
Ref: Build Real Web App with React #02
[index.js]
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route } from "react-router-dom";
import "semantic-ui-css/semantic.min.css";
import { createStore, applyMiddleware } from "redux"; import { Provider } from "react-redux"; import thunk from "redux-thunk";
import decode from "jwt-decode";
import { composeWithDevTools } from "redux-devtools-extension";
import App from "./App";
import registerServiceWorker from "./registerServiceWorker";
import rootReducer from "./rootReducer";
import { userLoggedIn } from "./actions/auth";
import setAuthorizationHeader from "./utils/setAuthorizationHeader";
----------------------------------------------------------------------
const store = createStore(
rootReducer, # 树!暂时先不搭建 ---> (2)
composeWithDevTools(applyMiddleware(thunk)) # ---> (1)
);
----------------------------------------------------------------------
if (localStorage.bookwormJWT) {
const payload = decode(localStorage.bookwormJWT);
const user = {
token: localStorage.bookwormJWT,
email: payload.email,
confirmed: payload.confirmed
};
setAuthorizationHeader(localStorage.bookwormJWT);
store.dispatch(userLoggedIn(user));
}
ReactDOM.render(
<BrowserRouter>
<Providerstore={store}>
<Route component={App} />
</Provider>
</BrowserRouter>,
document.getElementById("root")
);
registerServiceWorker();
(1) 合并 sub-reducer。
[rootReducer.js]
import { combineReducers } from "redux";
import user from "./reducers/user";
import books from "./reducers/books";
export default combineReducers({
user,
books
});
(2) 实时的监控Redux的状态树的Store
Ref: Redux-devTools简单的使用

- Redux 调用 Login by connect(...)
10:22 / 34:47
[LoginPage.js]
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import LoginForm from "../forms/LoginForm";
import { login } from "../../actions/auth";
class LoginPage extends React.Component {
submit = data =>
this.props.login(data).then(() => this.props.history.push("/dashboard"));
--------------------------------------------------------------------------------
render() {
return (
<div>
<h1>Login page</h1>
<LoginForm submit={this.submit} />
<Link to="/forgot_password">Forgot Password?</Link>
</div>
);
}
}
/**
* shape 相似于包裹着几个属性的 object
*/
LoginPage.propTypes = { history: PropTypes.shape({
push: PropTypes.func.isRequired
}).isRequired,
login: PropTypes.func.isRequired
};
/**
* login 函数就放在了 LoginPage容器 里面
*/
export default connect(null, { login })(LoginPage);
[actions/auth.js]
export const login = credentials => dispatch =>
api.user.login(credentials).then( # 真正干活的地方
user => {
localStorage.bookwormJWT = user.token;
setAuthorizationHeader(user.token);
dispatch(userLoggedIn(user));
}
);
这里定义了动做信号。
export const userLoggedIn = user => ({
type: USER_LOGGED_IN,
user
});