这是一个比较完整的简单的react移动端项目,说起来页面少,其实,构思若是精巧,也并不容易做
先放源码:https://github.com/bailicangdu/react-pxq



接下来我们简单的看看代码
项目有用到react-redux和axios处理数据

//根index.js引用了一些基本的方法
//定义了渲染组件  <Component />
import React from 'react';
import ReactDOM from 'react-dom';
import Route from './router/';
import FastClick from 'fastclick';
import registerServiceWorker from './registerServiceWorker';
import { AppContainer } from 'react-hot-loader';
import {Provider} from 'react-redux';
import store from '@/store/store';
import './utils/setRem';
import './style/base.css';
//解决tab穿透的问题
FastClick.attach(document.body);// 监听state变化
// store.subscribe(() => {
//   console.log('store发生了变化');
// });const render = Component => {ReactDOM.render(//绑定redux、热加载<Provider store={store}><AppContainer><Component /></AppContainer></Provider>,document.getElementById('root'),)
}render(Route);// Webpack Hot Module Replacement API
if (module.hot) {module.hot.accept('./router/', () => {render(Route);})
}registerServiceWorker();
//src/utils/setRem.js
//定义的是单位的转换
(function(psdw){var dpr=0 , rem=0 , scale=0;var htmlDOM=document.documentElement;dpr=window.devicePixelRatio;var currentWidth=htmlDOM.clientWidth;scale=currentWidth/psdw;rem=psdw/10;rem=rem*scale;htmlDOM.style.fontSize=rem+'px';htmlDOM.setAttribute('data-dpr',dpr)
})(750)
//src/store/store.js
//与store相对的就是reducer和action
//这里用到的是react-thunk
import {createStore, combineReducers, applyMiddleware} from 'redux';
import * as home from './home/reducer';
import * as production from './production/reducer';
import thunk from 'redux-thunk';let store = createStore(combineReducers({...home, ...production}),applyMiddleware(thunk)
);export default store;
//reducer里面主要是纯函数
//src/store/home/reducer.js
import * as home from './action-type';let defaultState = {orderSum: '', //金额name: '', //姓名phoneNo: '', //手机号imgpath: '', //图片地址
}
// 首页表单数据
export const formData = (state = defaultState , action = {}) => {switch(action.type){case home.SAVEFORMDATA:return {...state, ...{[action.datatype]: action.value}};case home.SAVEIMG:return {...state, ...{imgpath: action.path}};case home.CLEARDATA:return {...state, ...defaultState};default:return state;}
}
//src/store/production/reducer.js
import * as pro from './action-type';
import Immutable from 'immutable';let defaultState = {/*** 商品数据* @type {Array}* example: [{*    product_id: 1, 商品ID *    product_name: "PaiBot(2G/32G)", 商品名称*    product_price: 2999, 商品价格*    commission: 200, 佣金*    selectStatus: false, 是否选择*    selectNum: 0, 选择数量* }]*/dataList: [],
}export const proData = (state = defaultState, action) => {let imuDataList;let imuItem;switch(action.type){case pro.GETPRODUCTION: return {...state, ...action}case pro.TOGGLESELECT://避免引用类型数据,使用immutable进行数据转换 imuDataList = Immutable.List(state.dataList);imuItem = Immutable.Map(state.dataList[action.index]);imuItem = imuItem.set('selectStatus', !imuItem.get('selectStatus'));imuDataList = imuDataList.set(action.index, imuItem);// redux必须返回一个新的statereturn {...state, ...{dataList: imuDataList.toJS()}};case pro.EDITPRODUCTION://避免引用类型数据,使用immutable进行数据转换 imuDataList = Immutable.List(state.dataList);imuItem = Immutable.Map(state.dataList[action.index]);imuItem = imuItem.set('selectNum', action.selectNum);imuDataList = imuDataList.set(action.index, imuItem);// redux必须返回一个新的statereturn {...state, ...{dataList: imuDataList.toJS()}};// 清空数据case pro.CLEARSELECTED:imuDataList = Immutable.fromJS(state.dataList);for (let i = 0; i < state.dataList.length; i++) {imuDataList = imuDataList.update(i, item => {item = item.set('selectStatus', false);item = item.set('selectNum', 0);return item})}return {...state, ...{dataList: imuDataList.toJS()}};default: return state;}
}
//action-type里面是对象
//src/store/production/action-type.js
// 保存商品数据
export const GETPRODUCTION = 'GETPRODUCTION';
// 选择商品
export const TOGGLESELECT = 'TOGGLESELECT';
// 编辑商品
export const EDITPRODUCTION = 'EDITPRODUCTION';
// 清空选择
export const CLEARSELECTED = 'CLEARSELECTED';
//action 里面通过dispatch把数据发送给其他数据
import * as pro from './action-type';
import API from '@/api/api';// 初始化获取商品数据,保存至redux
export const getProData = () => {// 返回函数,异步dispatchreturn async dispatch => {try{let result = await API.getProduction();result.map(item => {item.selectStatus = true;item.selectNum = 0;return item;})dispatch({type: pro.GETPRODUCTION,dataList: result,})}catch(err){console.error(err);}}
}// 选择商品
export const togSelectPro = index => {return {type: pro.TOGGLESELECT,index,}
}// 编辑商品
export const editPro = (index, selectNum) => {return {type: pro.EDITPRODUCTION,index,selectNum,}
}// 清空选择
export const clearSelected = () => {return {type: pro.CLEARSELECTED,}
}
//src/store/home/action-type.js
// 保存表单数据
export const SAVEFORMDATA = 'SAVEFORMDATA';
// 保存图片
export const SAVEIMG = 'SAVEIMG';
// 清空数据
export const CLEARDATA = 'CLEARDATA';
//src/store/home/action.js
import * as home from './action-type';// 保存表单数据
export const saveFormData = (value, datatype) => {return {type: home.SAVEFORMDATA,value,datatype,}
}// 保存图片地址
export const saveImg = path => {return {type: home.SAVEIMG,path,}
}// 保存图片地址
export const clearData = () => {return {type: home.CLEARDATA,}
}
//工具函数中定义了异步组件
//src/utils/asyncComponent.jsx
import React, { Component } from "react";export default function asyncComponent(importComponent) {class AsyncComponent extends Component {constructor(props) {super(props);this.state = {component: null};}async componentDidMount() {const { default: component } = await importComponent();this.setState({component});}render() {const C = this.state.component;return C ? <C {...this.props} /> : null;}}return AsyncComponent;
}
//路由部门,定义首页就home页面
//src/router/index.js
//路由配置是异步加载的
import React, { Component } from 'react';
import { HashRouter, Switch, Route, Redirect } from 'react-router-dom';
import asyncComponent from '@/utils/asyncComponent';import home from "@/pages/home/home";
const record = asyncComponent(() => import("@/pages/record/record"));
const helpcenter = asyncComponent(() => import("@/pages/helpcenter/helpcenter"));
const production = asyncComponent(() => import("@/pages/production/production"));
const balance = asyncComponent(() => import("@/pages/balance/balance"));// react-router4 不再推荐将所有路由规则放在同一个地方集中式路由,子路由应该由父组件动态配置,组件在哪里匹配就在哪里渲染,更加灵活
export default class RouteConfig extends Component{render(){return(<HashRouter><Switch><Route path="/" exact component={home} /><Route path="/record" component={record} /><Route path="/helpcenter" component={helpcenter} /><Route path="/production" component={production} /><Route path="/balance" component={balance} /><Redirect to="/" /></Switch></HashRouter>)}
}

有公共组件header和alert

//header组件
//src/components/header/header.jsx
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './header.less';export default class PublicHeader extends Component{static propTypes = {record: PropTypes.any,title: PropTypes.string.isRequired,confirm: PropTypes.any,}state = {navState: false, //导航栏是否显示};// 切换左侧导航栏状态toggleNav = () => {this.setState({navState: !this.state.navState});}// css动画组件设置为目标组件FirstChild = props => {const childrenArray = React.Children.toArray(props.children);return childrenArray[0] || null;}shouldComponentUpdate(nextProps, nextState) {return !is(fromJS(this.props), fromJS(nextProps))|| !is(fromJS(this.state),fromJS(nextState))}render(){return(<header className="header-container"><span className="header-slide-icon icon-catalog" onClick={this.toggleNav}></span><span className="header-title">{this.props.title}</span>{this.props.record&&<NavLink to="/record" exact className="header-link icon-jilu"></NavLink>}{this.props.confirm&&<NavLink to="/" exact className="header-link header-link-confim">确定</NavLink>}<ReactCSSTransitionGroupcomponent={this.FirstChild}transitionName="nav"transitionEnterTimeout={300}transitionLeaveTimeout={300}>{this.state.navState && <aside key='nav-slide' className="nav-slide-list" onClick={this.toggleNav}><NavLink to="/" exact className="nav-link icon-jiantou-copy-copy">首页</NavLink><NavLink to="/balance" exact className="nav-link icon-jiantou-copy-copy">提现</NavLink><NavLink to="/helpcenter" exact className="nav-link icon-jiantou-copy-copy">帮助中心</NavLink></aside>}</ReactCSSTransitionGroup></header>);}
}
//src/components/alert/alert.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { is, fromJS } from 'immutable';
import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './alert.less';export default class Alert extends Component{static propTypes = {closeAlert: PropTypes.func.isRequired,alertTip: PropTypes.string.isRequired,alertStatus: PropTypes.bool.isRequired,}// css动画组件设置为目标组件FirstChild = props => {const childrenArray = React.Children.toArray(props.children);return childrenArray[0] || null;}// 关闭弹框confirm = () => {this.props.closeAlert();}shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}render(){return (<ReactCSSTransitionGroupcomponent={this.FirstChild}transitionName="alert"transitionEnterTimeout={300}transitionLeaveTimeout={300}>{this.props.alertStatus&&<div className="alert-con"><div className="alert-context"><div className="alert-content-detail">{this.props.alertTip}</div><TouchableOpacity className="confirm-btn" clickCallBack={this.confirm}/></div></div>}</ReactCSSTransitionGroup>);}
}
//src/pages/home/home.jsx
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { is, fromJS } from 'immutable';
import PropTypes from 'prop-types';
import API from '@/api/api';
import envconfig from '@/envconfig/envconfig';
import { saveFormData, saveImg, clearData } from '@/store/home/action';
import { clearSelected } from '@/store/production/action';
import PublicHeader from '@/components/header/header';
import PublicAlert from '@/components/alert/alert';
import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
import mixin, { padStr } from '@/utils/mixin';
import './home.less';@mixin({padStr})
class Home extends Component {static propTypes = {formData: PropTypes.object.isRequired,saveFormData: PropTypes.func.isRequired,saveImg: PropTypes.func.isRequired,clearData: PropTypes.func.isRequired,clearSelected: PropTypes.func.isRequired,}state = {alertStatus: false, //弹框状态alertTip: '', //弹框提示文字}/*** 已选择的商品数据* @type {Array}*/selectedProList = []; /*** 将表单数据保存至redux,保留状态* @param  {string} type  数据类型 orderSum||name||phoneNo* @param  {object} event 事件对象*/handleInput = (type, event) => {let value = event.target.value;switch(type){case 'orderSum':value = value.replace(/\D/g, '');break;case 'name':break;case 'phoneNo':value = this.padStr(value.replace(/\D/g, ''), [3, 7], ' ', event.target);break;default:;}this.props.saveFormData(value, type);}/*上传图片,并将图片地址存到redux,保留状态*/uploadImg = async event => {try{let formdata = new FormData();formdata.append('file', event.target.files[0]);let result = await API.uploadImg({data: formdata});this.props.saveImg(envconfig.imgUrl + result.image_path);console.log(result);}catch(err){console.error(err);}}// 提交表单sumitForm = () => {const {orderSum, name, phoneNo} = this.props.formData;let alertTip = '';if(!orderSum.toString().length){alertTip = '请填写金额';}else if(!name.toString().length){alertTip = '请填写姓名';}else if(!phoneNo.toString().length){alertTip = '请填写正确的手机号';}else{alertTip = '添加数据成功';this.props.clearSelected();this.props.clearData();}this.setState({alertStatus: true,alertTip,})}// 关闭弹款closeAlert = () => {this.setState({alertStatus: false,alertTip: '',})}// 初始化数据,获取已选择的商品initData = props => {this.selectedProList = [];props.proData.dataList.forEach(item => {if(item.selectStatus && item.selectNum){this.selectedProList.push(item);}})}componentWillReceiveProps(nextProps){if(!is(fromJS(this.props.proData), fromJS(nextProps.proData))){this.initData(nextProps);}}shouldComponentUpdate(nextProps, nextState) {return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state),fromJS(nextState))}componentWillMount(){this.initData(this.props);}render() {return (<main className="home-container"><PublicHeader title='首页' record /><p className="common-title">请录入您的信息</p><form className="home-form"><div className="home-form-tiem"><span>销售金额:</span><input type="text" placeholder="请输入订单金额" value={this.props.formData.orderSum} onChange={this.handleInput.bind(this, 'orderSum')}/></div><div className="home-form-tiem"><span>客户姓名:</span><input type="text" placeholder="请输入客户姓名" value={this.props.formData.name} onChange={this.handleInput.bind(this, 'name')}/></div><div className="home-form-tiem"><span>客户电话:</span><input type="text" maxLength="13" placeholder="请输入客户电话" value={this.props.formData.phoneNo} onChange={this.handleInput.bind(this, 'phoneNo')}/></div></form><div><p className="common-title">请选择销售的产品</p><Link to="/production" className="common-select-btn">{this.selectedProList.length ? <ul className="selected-pro-list">{this.selectedProList.map((item, index) => {return <li key={index} className="selected-pro-item ellipsis">{item.product_name}x{item.selectNum}</li>})}</ul>:'选择产品'}</Link></div><div className="upload-img-con"><p className="common-title">请上传发票凭证</p><div className="file-lable"><span className="common-select-btn">上传图片</span><input type="file" onChange={this.uploadImg}/></div><img src={this.props.formData.imgpath} className="select-img" alt=""/></div><TouchableOpacity className="submit-btn" clickCallBack={this.sumitForm} text="提交" /><PublicAlert closeAlert={this.closeAlert} alertTip={this.state.alertTip} alertStatus={this.state.alertStatus} /></main>);}
}export default connect(state => ({formData: state.formData,proData: state.proData,
}), {saveFormData, saveImg,clearData,clearSelected,
})(Home);
//src/utils/mixin.js
export default methods => {return target => {Object.assign(target.prototype, methods);}
}/*** 字符串填充函数* @param  {string} value      目标字符串* @param  {array} position 需要填充的位置* @param  {string} padstr   填充字符串* @return {string}          返回目标字符串*/
export const padStr = (value, position, padstr, inputElement) => {position.forEach((item, index) => {if (value.length > item + index) {value = value.substring(0, item + index) + padstr + value.substring(item + index)}})value = value.trim();// 解决安卓部分浏览器插入空格后光标错位问题requestAnimationFrame(() => {inputElement.setSelectionRange(value.length, value.length); })return value;
}
//帮助中心
//src/pages/helpcenter/helpcenter.jsx
import React, { Component } from 'react';
import PublicHeader from '@/components/header/header';
import { is, fromJS } from 'immutable';
import './helpcenter.less';export default class HelpCenter extends Component {shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}render(){return (<main><PublicHeader title="帮助中心" record /><article className="context-con"><h2>介绍</h2><p>本项目主要用于理解 react 和 redux 的编译方式,以及 react + redux 之间的配合方式</p><h2>技术要点</h2><p>react:v16.2</p><p>redux:v3.7</p><p>webpack:v3.8</p><p>react-router:v4.2</p><p>ES 6/7/8</p><p>code split</p><p>hot loader</p><p>axios:v0.17</p><p>less:v2.7</p><p>immutable:v3.8</p><p>项目地址 <a href="https://github.com/bailicangdu/react-pxq">github</a></p></article></main>)}
}
//src/pages/production/production.jsx
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import { connect } from 'react-redux';
import { getProData, togSelectPro, editPro } from '@/store/production/action';
import PropTypes from 'prop-types';
import PublicHeader from '@/components/header/header';
import './production.less';class Production extends Component{static propTypes = {proData: PropTypes.object.isRequired,getProData: PropTypes.func.isRequired,togSelectPro: PropTypes.func.isRequired,editPro: PropTypes.func.isRequired,}/*** 添加或删减商品,交由redux进行数据处理,作为全局变量* @param  {int} index 编辑的商品索引* @param  {int} num   添加||删减的商品数量*/handleEdit = (index, num) => {let currentNum = this.props.proData.dataList[index].selectNum + num;if(currentNum < 0){return}this.props.editPro(index, currentNum);}// 选择商品,交由redux进行数据处理,作为全局变量togSelect = index => {this.props.togSelectPro(index);}shouldComponentUpdate(nextProps, nextState) {return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}componentDidMount(){if(!this.props.proData.dataList.length){this.props.getProData();}}render(){return (<main className="common-con-top"><PublicHeader title='首页' confirm /><section className="pro-list-con"><ul className="pro-list-ul">{this.props.proData.dataList.map((item, index) => {return <li className="pro-item" key={index}><div className="pro-item-select" onClick={this.togSelect.bind(this, index)}><span className={`icon-xuanze1 pro-select-status ${item.selectStatus? 'pro-selected': ''}`}></span><span className="pro-name">{item.product_name}</span></div><div className="pro-item-edit"><span className={`icon-jian ${item.selectNum > 0? 'edit-active':''}`} onClick={this.handleEdit.bind(this, index, -1)}></span><span className="pro-num">{item.selectNum}</span><span className={`icon-jia`} onClick={this.handleEdit.bind(this, index, 1)}></span></div></li>})}</ul></section></main>)}
}export default connect(state => ({proData: state.proData,
}), {getProData, togSelectPro, editPro
})(Production);

//src/pages/balance/balance.jsx
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import PublicHeader from '@/components/header/header';
import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
import PublicAlert from '@/components/alert/alert';
import API from '@/api/api';
import './balance.less';class BrokeRage extends Component{state = {applyNum: '', //输入值alertStatus: false, //弹框状态alertTip: '', //弹框提示文字balance: {  //可提现金额balance: 0, },}// 初始化数据initData = async () => {try{let result = await API.getBalance();console.log(result);this.setState({balance: result});}catch(err){console.error(err);}}/*** 格式化输入数据* 格式为微信红包格式:最大 200.00* @param  {object} event 事件对象*/handleInput = event => {let value = event.target.value;if((/^\d*?\.?\d{0,2}?$/gi).test(value)){if((/^0+[1-9]+/).test(value)) {value = value.replace(/^0+/,'');}if((/^0{2}\./).test(value)) {value = value.replace(/^0+/,'0');}value = value.replace(/^\./gi,'0.');if(parseFloat(value) > 200){value = '200.00';}this.setState({applyNum: value});}}/*** 提交判断条件*/sumitForm = () => {let alertTip;if(!this.state.applyNum){alertTip = '请输入提现金额';}else if(parseFloat(this.state.applyNum) > this.state.balance.balance){alertTip = '申请提现金额不能大于余额';}else{alertTip = '申请提现成功';}this.setState({alertStatus: true,alertTip,applyNum: '',})}/*关闭弹框*/closeAlert = () => {this.setState({alertStatus: false,alertTip: '',})}shouldComponentUpdate(nextProps, nextState) {return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state),fromJS(nextState))}componentDidMount(){this.initData();}render(){return (<main className="home-container"><PublicHeader title='提现' record /><section className="broke-main-content"><p className="broke-header">您的可提现金额为:¥ {this.state.balance.balance}</p><form className="broke-form"><p>请输入提现金额(元)</p><p>¥ <input type="text" value={this.state.applyNum} placeholder="0.00" onInput={this.handleInput} maxLength="5" /></p></form><TouchableOpacity className="submit-btn" clickCallBack={this.sumitForm} text="申请提现" /></section><PublicAlert closeAlert={this.closeAlert} alertTip={this.state.alertTip} alertStatus={this.state.alertStatus} /></main>);}
}export default BrokeRage;

//src/pages/record/components/recordList.jsx
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import API from '@/api/api';
import './recordList.less';class RecordList extends Component{state = {recordData: [],}/*** 初始化获取数据* @param  {string} type 数据类型*/getRecord = async type => {try{let result = await API.getRecord({type});this.setState({recordData: result.data||[]})}catch(err){console.error(err);}}componentWillReceiveProps(nextProps){// 判断类型是否重复let currenType = this.props.location.pathname.split('/')[2];let type = nextProps.location.pathname.split('/')[2];if(currenType !== type){this.getRecord(type);}}shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}componentWillMount(){let type = this.props.location.pathname.split('/')[2];this.getRecord(type);}render(){return (<div>{/* 这个记录页面与数据的渲染形成整体的组件 */}<ul className="record-list-con">{this.state.recordData.map((item, index) => {return <li className="record-item" key={index}><section className="record-item-header"><span>创建时间:{item.created_at}</span><span>{item.type_name}</span></section><section className="record-item-content"><p><span>用户名:</span>{item.customers_name} &emsp; {item.customers_phone}</p><p><span>商&emsp;品:</span>{item.product[0].product_name}</p><p><span>金&emsp;额:</span>{item.sales_money} &emsp; 佣金:{item.commission}</p></section><p className="record-item-footer">等待管理员审核,审核通过后,佣金将结算至账户</p></li>})}</ul></div>);}}export default RecordList;

//src/pages/record/record.jsx
import React, { Component } from 'react';
import { is, fromJS } from 'immutable';
import { NavLink, Switch, Route, Redirect } from 'react-router-dom';
import PublicHeader from '@/components/header/header';
import RecordList from './components/recordList';
import './record.less';class Record extends Component {state = {flagBarPos: '17%',}/*** 设置头部底部标签位置* @param  {string} type 数据类型*/setFlagBarPos = type => {let flagBarPos;switch(type){case 'passed':flagBarPos = '17%';break;case 'audited':flagBarPos = '50%';break;case 'failed':flagBarPos = '83%';break;default: flagBarPos = '17%';}this.setState({flagBarPos})}componentWillReceiveProps(nextProps){// 属性变化时设置头部底部标签位置let currenType = this.props.location.pathname.split('/')[2];let type = nextProps.location.pathname.split('/')[2];if(currenType !== type){this.setFlagBarPos(type);}}shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}componentWillMount(){// 初始化设置头部底部标签位置let type = this.props.location.pathname.split('/')[2];this.setFlagBarPos(type);}render() {return (<main className="common-con-top"><PublicHeader title='记录' /><section className="record-nav-con"><nav className="record-nav"><NavLink to={`${this.props.match.path}/passed`} className="nav-link">已通过</NavLink><NavLink to={`${this.props.match.path}/audited`} className="nav-link">待审核</NavLink><NavLink to={`${this.props.match.path}/failed`} className="nav-link">未通过</NavLink></nav><i className="nav-flag-bar" style={{left: this.state.flagBarPos}}></i></section>{/* 子路由在父级配置,react-router4新特性,更加灵活 */}<Switch><Route path={`${this.props.match.path}/:type`} component={RecordList} /><Redirect from={`${this.props.match.path}`} to={`${this.props.match.path}/passed`} exact component={RecordList} /></Switch></main>);}
}export default Record;

转载于:https://www.cnblogs.com/smart-girl/p/10905429.html

【心有猛虎】react-pxq相关推荐

  1. React实战精讲(React_TS/API)

    英国诗人 萨松在诗歌 <与我,过去.现在以及未来>中写道:"In me the tiger sniffs the rose" 诗人余光中将其翻译为:"心有猛虎 ...

  2. react非常适合入门者学习使用的后台管理框架

    项目简介 该项目提供一个非常简洁的后台管理ui界面,非常适合初学者学习使用.项目结构: 项目地址:GitHub项目地址 技术栈 - react - antd - react-router-dom - ...

  3. 在 react 里使用 antd

    在 react 里使用 antd 在 powershell 里npm i antd 引入方式: import '../node_modules/antd/dist/antd.css'

  4. 在React Hook里使用history.push跳转

    在React Hook里使用history.push跳转 react hook里用不了this.props.history的解决方法 首先引入 import { useHistory } from ' ...

  5. 在react hook里使用mobx(配置mobx依赖)

    在powershell里安装依赖 (直接npm i mobx或者npm i mobx-react是会报错的) npm i mobx mobx-react --save save是下载到"de ...

  6. React router 的 Route 中 component 和 render 属性理解

    React router 的 Route 中 component 和 render 属性理解 Route 标签的三个互斥属性 render.component.children Route 就是用来匹 ...

  7. react 子组件获取变量属性值

    刚刚遇到一个问题:子组件属性值绑定了变量,但是在子组件的componentDidMoiunt中拿到的值始终是undefinded.如下: 1 <PieInfo 2 title='有效病案' 3 ...

  8. 超简单的react和typescript和引入scss项目搭建流程

    1.首先我们先创建一个react项目,react官网也有react项目搭建的命令 npx create-react-app my-app cd my-app 2.安装我们项目需要的样式依赖,这个项目我 ...

  9. React Native之ViewPagerAndroid跳转页面问题

    前言: 网上目前react-native的教程较少,加上许多帖子还是用的ES5(2015年6月已发布ES6标准),有些细节很难找到答案,这里把遇到的问题做一个分享,让学习者尽量少踩坑. 出现问题: 1 ...

  10. http状态404 vscode_VS Code 调试完全攻略(5):基于浏览器的 React 应用

    每日前端夜话第344篇 翻译:疯狂的技术宅 作者:Charles Szilagyi 来源:charlesagile 正文共:1750 字 预计阅读时间:7 分钟 这次我们来研究怎样把调试器连接到用 C ...

最新文章

  1. h5新增浏览器本地缓存localStorage
  2. 为AD用户启用或禁用OCS 2007 R2帐户
  3. linux中进程的用户管理
  4. JS-以鼠标位置为中心的滑轮放大功能demo1
  5. java登陆拦截器_SpringBoot拦截器实现登录拦截
  6. ASP.NET Web应用程序和ASP.NET网站的区别
  7. 招聘:以考试为主,面试为辅
  8. linux系统鼠标主题下载,Ubuntu Linux鼠标主题:AlkanoMB
  9. 如何查看Websphere(was)上的jdk版本
  10. 她受马云影响创业,想在全球女性创业者大会上分享这些事
  11. 用防火墙自动拦截攻击IP
  12. 黑龙江省大学计算机学校排名2015,2015黑龙江省大学排行榜 哈工大第一
  13. [MySQL系列] SELECT STRAIGHT_JOIN优化join查询技巧
  14. 有趣且有意义的数字,你想到了什么?请不吝留言
  15. Photo Album: 2008年5月-三亚爱琴海岸康年度假村-day2
  16. 基于FPGA的4位减法器设计及仿真代码
  17. Appium+Python appium启动夜神模拟器定位元素(三)
  18. stm32 ADC hal库实现
  19. 在计算机中 汉字系统把一个汉字表示为,计算机问题汉字系统在计算机内把一个汉字表示 – 手机爱问...
  20. [Qt] Qt自带的modbus client 和 server 例程的拓展

热门文章

  1. unity游戏开发日志(一)将mmd模型导入unity,并解决材质丢失的问题
  2. 139、商城业务-nginx-搭建域名访问环境一(反向代理配置)
  3. c语言:动态输入字符串数组
  4. 全志平台双路LVDS配置
  5. VC++ 读取数据库datetime类型的处理
  6. 工作压力是恶魔——华为跳楼员工身份确认 生前念大学留下大量债务
  7. React教程(详细版)
  8. HDU 3664 Permutation Counting
  9. routes 选项参数
  10. 设计模式面试|Java面试题