รายละเอียดสำหรับเรียนรู้ React ปี 2020-2023 โดยโค้ชพล ดูหลักสูตรได้ที่ https://www.nextflow.in.th/react-training
npm i react-router-dom connected-react-router
สร้างไฟล์ src/redux/root.reducer.js
ไฟล์นี้จะเป็นตัวรวม reducer ในแอพของเรา และสามารถสร้างเพิ่มได้
แน่นอนว่าตอนนี้เรามีแค่ dashboardReducer
และเราจะเพิ่ม reducer ของ react-router
ที่ชื่อ connectRouter()
เข้ามา
สังเกตว่าเราตั้งชื่อให้กับ reducer แต่ละตัวได้ด้วย
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'
import dashboardReducer from "./dashboard.reducer";
export default (history) => combineReducers({
router: connectRouter(history),
dashboard: dashboardReducer
})
เปิดไฟล์ src/redux/store.js
เร่ิมแรกเราจะเอา History Module เข้ามาใช้
import { createBrowserHistory } from 'history'
export const history = createBrowserHistory()
จากนั้น เราจะเอา rootReducer
มาใช้แทน dashboardReducer
import createRootReducer from "./root.reducer";
export default function configureStore() {
const store = createStore(
createRootReducer(history),
applyMiddleware(thunk,logger),
),
);
return store;
}
และเราจะใช้ compose
จาก redux เป็นตัวประกอบ middleware เดิม และ middleware ของ react-router
เข้าไปใน store
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware } from 'connected-react-router'
const store = createStore(
createRootReducer(history),
compose(
applyMiddleware(
routerMiddleware(history),
thunk,
logger
),
),
);
import { createStore, applyMiddleware, compose } from 'redux';
import dashboardReducer from "./dashboard.reducer";
import thunk from 'redux-thunk';
import { logger } from 'redux-logger';
import { routerMiddleware } from 'connected-react-router'
import { createBrowserHistory } from 'history'
import createRootReducer from "./root.reducer";
export const history = createBrowserHistory()
export default function configureStore() {
const store = createStore(
createRootReducer(history),
compose(
applyMiddleware(
routerMiddleware(history),
thunk,
logger
),
),
);
return store;
}
สร้างไฟล์ src/pages/Dashboard.js
ใช้ snippet ได้
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import MapBranch from '../components/MapBranch';
import StatChart from '../components/StatChart';
import { Layout, Row, Col } from 'antd';
import actions from '../redux/actions';
const { Header, Content, Footer } = Layout;
export class Dashboard extends Component {
static propTypes = {
prop: PropTypes
}
render() {
return (
<Row gutter={16}>
<Col span={12}><MapBranch /></Col>
<Col span={12}><StatChart /></Col>
</Row>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = {
}
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard)
ปรับไฟล์ App.js ใหม่
import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import HeaderBar from './components/HeaderBar';
import MapBranch from './components/MapBranch';
import StatChart from './components/StatChart';
import { Layout, Menu, Row, Col, message } from 'antd';
import actions from './redux/actions';
const { Header, Content, Footer } = Layout;
class App extends Component {
componentDidMount() {
this.props.initData();
}
componentDidUpdate() {
if (this.props.notification.isShow) {
if (this.props.notification.isError) {
message.error(this.props.notification.message);
} else {
message.success(this.props.notification.message);
}
}
}
render() {
return (
<div>
<Layout className="layout">
<HeaderBar />
<Content style=>
<div
style=>
</div>
</Content>
<Footer style=>React Redux Workshop ©2012-2019 Created by Nextflow.in.th</Footer>
</Layout>,
</div>
);
}
}
const mapStateToProps = (state) => {
console.log(state)
return {
notification: state.app.notification
}
}
const mapDispatchToProps = dispatch => {
return {
initData: () => dispatch(actions.requestInitData())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
เปิดไฟล์ src\index.js
เริ่มจาก import ส่วนที่ต้องการเข้ามาใช้งาน
import configureStore, { history } from "./redux/store";
import { ConnectedRouter } from 'connected-react-router'
และครอบ App ของเราด้วย <ConnectedRouter>
ReactDOM.render((
<Provider store={store}>
<ConnectedRouter history={history}>
<App/>
</ConnectedRouter>
</Provider>
), document.getElementById('root'));
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Provider} from 'react-redux'
import configureStore, { history } from "./redux/store";
import { ConnectedRouter } from 'connected-react-router'
const store = configureStore();
ReactDOM.render((
<Provider store={store}>
<ConnectedRouter history={history}>
<App/>
</ConnectedRouter>
</Provider>
), document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls. Learn
// more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
เปิดไฟล์ src/App.js
เราจะมีการ import Component ที่ต้องใช้มาจาก react-router-dom
import { Route, Switch } from "react-router-dom";
และ Dashboard component
import { Dashboard } from './pages/Dashboard';
ทำให้เราสามารถวางระบบ Route เพื่อจัดการแสดงหน้าตาของแอพได้
<Switch>
<Route path="/" exact component={Dashboard} />
</Switch>
import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { Route, Switch } from "react-router-dom";
import HeaderBar from './components/HeaderBar';
import MapBranch from './components/MapBranch';
import StatChart from './components/StatChart';
import { Layout, Menu, Row, Col, message } from 'antd';
import actions from './redux/actions';
import { Dashboard } from './pages/Dashboard';
const { Header, Content, Footer } = Layout;
class App extends Component {
componentDidMount() {
this.props.initData();
}
componentDidUpdate() {
if (this.props.notification.isShow) {
if (this.props.notification.isError) {
message.error(this.props.notification.message);
} else {
message.success(this.props.notification.message);
}
}
}
render() {
return (
<div>
<Layout className="layout">
<HeaderBar />
<Content style=>
<div
style=>
<Switch>
<Route path="/" exact component={Dashboard} />
</Switch>
</div>
</Content>
<Footer style=>React Redux Workshop ©2012-2019 Created by Nextflow.in.th</Footer>
</Layout>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log(state)
return {
notification: state.app.notification
}
}
const mapDispatchToProps = dispatch => {
return {
initData: () => dispatch(actions.requestInitData())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
จะเห็นว่าหลังจากเราใช้ combineReducer
แล้วตัว Component ของเราจะพังในส่วน mapStateToProps
เป็นแถบๆ
เพราะในที่นี้ state object ที่ส่งมาเข้า mapStateToProps
จะได้เป็นโครงสร้างใหม่ ตามที่เราตั้งชื่อไว้ในไฟล์ root.reducer.js
src/App.js
const mapStateToProps = (state) => {
console.log(state)
return {
notification: state.dashboard.app.notification
}
}
src/components/MapBranch.js
const mapStateToProps = (state) => ({
branches: state.dashboard.branches
})
src/components/StatChart.js
const mapStateToProps = (state) => ({
chartData: state.dashboard.branchDataInChart
})
สร้างไฟล์ src/pages/LoginPage.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
export class LoginPage extends Component {
render() {
return (
<div>
Log me in
</div>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = {
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage)
import React, { Component } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { Route, Switch } from "react-router-dom";
import HeaderBar from './components/HeaderBar';
import MapBranch from './components/MapBranch';
import StatChart from './components/StatChart';
import { Layout, Menu, Row, Col, message } from 'antd';
import actions from './redux/actions';
import Dashboard from './pages/Dashboard';
import LoginPage from './pages/LoginPage';
const { Header, Content, Footer } = Layout;
class App extends Component {
componentDidMount() {
this.props.initData();
}
componentDidUpdate() {
if (this.props.notification.isShow) {
if (this.props.notification.isError) {
message.error(this.props.notification.message);
} else {
message.success(this.props.notification.message);
}
}
}
render() {
return (
<div>
<Layout className="layout">
<HeaderBar />
<Content style=>
<div
style=>
<Switch>
<Route path="/" exact component={LoginPage} />
<Route path="/dashboard" exact component={Dashboard} />
</Switch>
</div>
</Content>
<Footer style=>React Redux Workshop ©2012-2019 Created by Nextflow.in.th</Footer>
</Layout>
</div>
);
}
}
const mapStateToProps = (state) => {
console.log(state)
return {
notification: state.app.notification
}
}
const mapDispatchToProps = dispatch => {
return {
initData: () => dispatch(actions.requestInitData())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Form, Icon, Input, Button, Checkbox } from 'antd';
export class LoginPage extends Component {
render() {
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
<Input
prefix={<Icon type="user" style= />}
placeholder="Username"
/>
</Form.Item>
<Form.Item>
<Input
prefix={<Icon type="lock" style= />}
type="password"
placeholder="Password"
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
</Form.Item>
</Form>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = {
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage)
เปิดไฟล์ src/pages/LoginPage.js
เริ่มแรกจากการกำหนดค่าเริ่มต้นให้กับ State
state = {
username: '',
password: ''
}
จากนั้นสร้าง method function สำหรับใช้ทำงานตอนที่ผู้ใช้กรอกข้อมูล
setUsername = (value) => {
this.setState({
...this.state,
username: value
})
// console.log(this.state);
}
setPassword = (value) => {
this.setState({
...this.state,
password: value
})
// console.log(this.state);
}
จากนั้นใน <Input>
:
value
เชื่อมกับค่าจาก StateonInput
ให้เชื่อมกับ method ของเรา เพื่ออัพเดตข้อมูลเข้า State เวลาที่ผู้ใช้กรอกข้อมูล<Input
...
value={this.state.username}
onInput={e => this.setUsername(e.target.value)}
/>
<Input
...
value={this.state.password}
onInput={e => this.setPassword(e.target.value)}
/>
ไฟล์เต็ม src/pages/LoginPage.js
import React, { Component, useState } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Form, Icon, Input, Button, Checkbox } from 'antd';
export class LoginPage extends Component {
state = {
username: '',
password: ''
}
handleSubmit = (e) => {
e.preventDefault();
console.log(`Loggin in with U:${this.state.username} P:${this.state.password}`)
}
setUsername = (value) => {
this.setState({
...this.state,
username: value
})
// console.log(this.state);
}
setPassword = (value) => {
this.setState({
...this.state,
password: value
})
// console.log(this.state);
}
render() {
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
<Input
prefix={<Icon type="user" style= />}
placeholder="Username"
value={this.state.username}
onInput={e => this.setUsername(e.target.value)}
/>
</Form.Item>
<Form.Item>
<Input
prefix={<Icon type="lock" style= />}
type="password"
placeholder="Password"
value={this.state.password}
onInput={e => this.setPassword(e.target.value)}
/>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
</Form.Item>
</Form>
)
}
}
const mapStateToProps = (state) => ({
})
const mapDispatchToProps = {
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginPage)