Skip to content

MiaLi7/react-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

基于create-react-app引入router、redux、saga的脚手架搭建

一、背景介绍

之前我们已经试过基于create-react-app分别单独引入router、redux、saga搭建的脚手架,现在我们把router、redux、saga三个一起引入,并基于它们做一个简单的例子测试一下。

例子中共分为两个组件,即两个不同的页面,通过router实现不同页面的跳转,有一个页面使用redux做一个简单的计数器,另一个页面使用saga实现异步请求获取远端服务器的数据,并把获取到的数据显示出来。下面我们具体看看如何一起引入router、redux、saga搭建脚手架。

二、实现步骤

1、创建项目

选择你想要存放项目的路径,假定当前所在的文件目录为 D:\work\workspace\react ,在目录空白处按住键盘shift键,同时鼠标右键,选择“在此处打开命令窗口”,在打开的命令行窗口中输入命令

$ create-react-app react-demo

,其中react-demo是你想创建的项目名字,输入完全后,按回车,可以看到命令行窗口一直在跳动,这样create-react-app就会自动帮我们下载项目所要依赖的文件了,我们只要等待项目创建完成就可以了。当命令行窗口出现Happy hacking!,即项目创建完成,我们可以在D:\work\workspace\react目录下发现该目录下多了一个react-demo的文件夹,这就是我们创建的项目了。

2、项目文件目录结构

├── node_modules                            // 项目第三方依赖文件
├── public                                  // 放静态资源
├── src                                     // 源码目录
│   ├── App.css                             // 组件样式
│   ├── App.js                              // 组件文件
│   ├── App.test.js                         // 组件测试文件
│   ├── index.css                           // 项目入口文件样式
│   ├── index.js                            // 项目入口文件
│   ├── logo.svg                            // 项目图标文件
│   ├── serviceWorker.js                    // 资源缓存
├── .gitignore                              // 告诉Git哪些文件不需要添加到版本管理中
├── package-lock.json                       // 锁定安装时的包的版本号
├── package.json                            // 项目配置文件,项目依赖包版本号
├── README.md                               // 项目的说明文件

3、启动项目

创建好项目后,需要先启动项目查看项目是否能够正常运行。在命令行窗口中输入命令

$ cd react-demo

进入项目,再输入命令

$ npm start

启动项目,项目启动后会自动打开一个浏览器窗口加载页面,则项目启动完成。当浏览器加载页面情况如下图代表启动成功。

启动成功

4、下载相关依赖

先关闭刚才启动的项目,在命令行窗口,同时按住键盘ctrl+C按键,在显示的命令处输入“y”即可关闭项目。今天我们做的项目分别都要用到router、redux、saga、axios,所以要下载相关的依赖文件。在命令行窗口依次输入

$ npm install react-router-dom --save
$ npm install redux --save
$ npm install react-redux --save
$ npm install redux-saga --save
$ npm install axios --save

下载完成后,打开package.json文件,即可发现在dependencies中多了这些依赖版本号。 依赖

下载完依赖,就可以启动项目了,在命令行窗口输入:

npm start

5、具体实现过程

(1). 引入redux,实现计数器组件

在src文件夹下新建一个文件夹components,用来存放自定义的组件及相关逻辑处理,在components文件下新建一个文件夹Counter,用来存放计数器组件。在Couter文件夹下新建文件index.js,定义计数器组件,并将组件与store建立连接。

src/components/Counter/index.js代码如下

import React, { Component } from 'react';
import './index.css';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';

class Counter extends Component {
  constructor( props) {
	super( props );
	this.state = {
	  counterNum: 0
	}
  }

  handleAdd() {
	this.props.dispatch({ type: 'ADD'});
  }

  handleSub() {
	this.props.dispatch({ type: 'SUB'});
  }

  render() {
	return (
	  <div className="counter">
		<button style={{fontSize: '18px', height: '47px', width: '147px'}}>
		  <Link to="/getSaga" style={{textDecoration: 'none', color: 'white'}}>跳转到第二页面</Link>
		</button>
		<br />
		<h1> { this.props.counterNum ?  this.props.counterNum : this.state.counterNum} </h1>
		<button onClick={this.handleAdd.bind(this)}>增加</button>
		<button onClick={this.handleSub.bind(this)}>减少</button>

	  </div>
	);
  }
}
const mapStateToProps = ( state ) => {
  return ({
	counterNum: state.handleCounter
  });
}

export default connect( mapStateToProps )(Counter);

为了使页面的样式好看,在Counter文件夹下新建文件index.css

src/components/Counter/index.css代码如下

.counter {
	height: 300px;
	width: 500px;

	text-align: center;
	margin: 0 auto;
}

.counter button {
	margin-left: 27px;
	background-color: rgba(81,182,254,.7); 
	width: 85px;
	height: 42px;
	border-radius: 5px;
	color: white;
	font-size: 18px;
	margin-top: 30px;
}

(2). 处理计数器组件的reducer

由于考虑到多页面,多组件,同时也为了体现组件化思想,所以这里我把属于哪个组件的reducer就放在哪个组件下,最后再通过一个总的reducers文件把所有的reducer导出来。所以Counter组件的reducer,就在Counter文件夹下新建一个reducer文件夹,里面新建文件reducer.js

src/components/Counter/reducer/reducer.js代码如下:

export function handleCounter(state = 0, action ) {
	switch( action.type) {
		case 'ADD':
			return state + 1;
		case 'SUB':
			return state - 1;
		default:
			return state;
	}
}

到这里,对于Counter计数器组件的定义工作大体完成了,下面我们继续定义第二个组件,获取saga数据。

(3). 定义saga组件

因为saga组件是与Counter组件并列,所以在components文件夹下新建文件夹GetSagaVal,在GetSagaVal文件夹下新建文件index.js

src/components/GetSagaVal/index.js代码如下:

import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';

class GetSagaVal extends React.Component {

  handleClick() {
	this.props.dispatch({ type: 'GET'});
  }

  render() {
	console.log(this.props.sagaval.data[0]);
	return (
	  <div style={{marginLeft: '20px'}}>
		<h4>请点击按钮,获取第一条saga数据的名字</h4>
		<button onClick={this.handleClick.bind(this)}>获取saga数据</button>
		<h4>{ this.props.sagaval.data.length > 0 ? this.props.sagaval.data[0].name : '无'}</h4>
		<br />
		<button style={{fontSize: '18px', height: '47px', width: '147px', backgroundColor: 'rgba(81,182,254,.7)'}}>
		  <Link to="/" style={{textDecoration: 'none', color: 'white'}}>跳转到第一页面</Link>
		</button>

	  </div>
	);
  }

}
const mapStateToProps = (state) => {
  return ({
	sagaval: state.getSaga
  });
}

export default connect( mapStateToProps )(GetSagaVal);

(4). 定义saga的generator函数,执行异步请求

由于组件化思想,同时也为了方便管理组件,所以saga也是放到对应的组件下面进行管理。在GetSagaVal文件夹下新建文件夹sagas,在里面新建文件saga.js

src/components/Counter/sagas/saga.js代码如下:

import { put, call, takeEvery } from 'redux-saga/effects';
import axios from 'axios';

function* workerSaga() {
  const data = yield axios({
	method: 'get',
	url: `https://jsonplaceholder.typicode.com/users`
  });
  console.log( data );
  yield put({ type: "GETSAGA", data: data});
}


function* watchSaga() {
  yield takeEvery('GET', workerSaga);
}
export default watchSaga;

(5). 处理GetSagaVal组件的reducer

GetSagaVal文件夹下新建文件夹reducer,里面新建文件reducer.js

src/components/GetSagaVal/reducer/reducer.js代码如下:

import React from 'react';
import { combineReducers } from 'redux';

export function getSaga( state = {data: []}, action) {
	switch( action.type) {
		case 'GETSAGA':
			return action.data;
		default:
			return state;
	}
}

(6). 把所有组件的reducer通过一个文件导出来

之前为了方便管理,所以都是把每个组件对应的reducer放在相应的组件下进行管理,由于入口文件创建store时要用到所有的reducer,所以要把所有组件的reducer进行导出。 在src文件夹下新建文件reducers.js

src/reducers.js代码如下:

import { combineReducers } from 'redux';
import * as counterReducer from './components/Counter/reducer/reducer.js';
import * as getSagaReducer from './components/GetSagaVal/reducer/reducer.js';

// 通过一系列异步加载来创建总reducers
export default combineReducers({
	...counterReducer,
	...getSagaReducer
});

(7). 在入口文件创建store,定义路由

修改入口文件index.js,并且定义路由。

src/index.js代码如下:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import reducer from './reducers.js';
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import createSagaMiddleware from 'redux-saga';
import Counter from './components/Counter/index.js';
import GetSagaVal from './components/GetSagaVal/index.js';
import saga from './components/GetSagaVal/sagas/saga.js';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(
	reducer,
	applyMiddleware( sagaMiddleware )
);
sagaMiddleware.run( saga );

ReactDOM.render(
	<Provider store={ store }>
		<Router>
			<Route exact path="/" component={ Counter }/>
			<Route path="/getSaga" component={ GetSagaVal }/>
		</Router>
	</Provider>,
	document.getElementById('root')
);

serviceWorker.unregister();

到这里,这个例子就已经完成了,把router、redux、saga、axios都引进去了。

具体文件结构如下: 文件结构

三、实现效果

刷新页面,可以看到页面初始加载的是计数器,点击增加或减少按钮,计数器的数字会相应改变,点击“跳转到第二页面按钮”,则会跳转到请求saga的页面,注意,url地址栏也发生了改变,一开始没有saga数据时,显示的是“无”,当点击“获取saga数据”按钮时,页面发生了改变,显示出了第一个saga数据的名字,点击“跳转到第一页面”按钮,则会返回到计数器页面,实现不同页面间的跳转。

页面1

页面2

saga请求

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published