前言
1、故事背景
Build App with Reactcss
Project系列,主要是從實踐的角度來分析需求,設計組件,設計網頁。html
![](http://static.javashuo.com/static/loading.gif)
2、組件設計綱要
/* implement */react
代碼分析
1、項目配置問題
.eslintrc文件採用airbnb的嚴格檢查模式。json
![](http://static.javashuo.com/static/loading.gif)
package.json中添加:redux
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
快捷鍵直接獲得以下代碼模板:api
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
點擊後出現顏色值提取器,以下:react-router
![](http://static.javashuo.com/static/loading.gif)
填下以下,這裏使用了 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
![](http://static.javashuo.com/static/loading.gif)
(2) 配置主頁 HomePage
a. 打算 導入HomePage.js,並做爲路由根路徑。path = "/" exact
b. 下一步,就是實現HomePage,至少包括了login的連接按鈕。
[HomePage.js]
![](http://static.javashuo.com/static/loading.gif)
(3) 實現主頁 HomePage,以及其中的 Link。
[HomePage.js]
![](http://static.javashuo.com/static/loading.gif)
(4) 實現 LoginPage
這裏主要是添加上對應的路由。
[App.js]
![](http://static.javashuo.com/static/loading.gif)
以上便實現了一個基本的路由過程。
爲何要用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上,以後須要系統學習。
![](http://static.javashuo.com/static/loading.gif)
(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>
格式錯誤後出現小提示。
![](http://static.javashuo.com/static/loading.gif)
(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會報警告以下:
![](http://static.javashuo.com/static/loading.gif)
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簡單的使用
![](http://static.javashuo.com/static/loading.gif)
- 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
});