Ant Design of React
目录
- 1 常用代码
- 2 常用链接
- 3 G6
- 4 useEffect,useState
- 5 demo
- 6 常用命令
- 7 常用链接
- 8 ant-design-pro
- 9 初始项目
- 10 组件
- 11 编码规范
- 12 React
- 13 Redux
- 14 React-Redux
- 15 roadhog
- 16 dva
- 17 dva cil
- 18 组件
- 19 组件属性获取与传递/设置
- 20 子组件与父组件
- 21 常用代码
- 22 基础知识
- 23 合并DBTable
- 24 部署
- 25 常见问题
- 25.1 浏览器路径问题
- 25.2 antd没有样式
- 25.3 TypeScript
- 25.4 setState后获取到的thisstate没变,还是初始state
- 25.5 正确显示头行结构的面包屑
- 25.6 Modal内容不清空
- 25.7 modal有时显示,有时不显示
- 25.8 table datasouce明明有多条数据却只显示一条
- 25.9 menu可以打开,刷新打开报错
- 25.10 Failed to execute 'pushState' on 'History' error when using window.history.pushState function
- 25.11 tab显示
- 26 参考
常用代码
{JSON.stringify(values)}
常用链接
https://umijs.org/zh-CN/config#chunks
https://zh-hans.reactjs.org/docs/getting-started.html
https://ant.design/docs/react/introduce-cn
https://pro.ant.design/docs/getting-started-cn
https://blog.csdn.net/weixin_41753520/article/details/98317567
https://antv-g6.gitee.io/zh/docs/manual/introduction
https://graphin.antv.vision/zh/docs/manual/getting-started/
https://wiki.ling2.cn/index.php/Segmenter_user.dict
G6
https://graphin.antv.vision/zh/docs/manual/getting-started
useEffect,useState
https://blog.csdn.net/gtLBTNq9mr3/article/details/100059135
// 声明一个叫 “count” 的 state 变量
const [count, setCount] = useState(0);
既然我们知道了 useState 的作用,那么掌握 useEffect 就更容易,函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
demo
import React from 'react';
import { Spin } from 'antd';
import isEqual from 'lodash/isEqual';
import { isComponentClass } from './Secured';
// eslint-disable-next-line import/no-cycle
interface PromiseRenderProps<T, K> {
ok: T;
error: K;
promise: Promise<boolean>;
}
interface PromiseRenderState {
component: React.ComponentClass | React.FunctionComponent;
}
export default class PromiseRender<T, K> extends React.Component<
PromiseRenderProps<T, K>,
PromiseRenderState
> {
state: PromiseRenderState = {
component: () => null,
};
componentDidMount() {
this.setRenderComponent(this.props);
}
shouldComponentUpdate = (nextProps: PromiseRenderProps<T, K>, nextState: PromiseRenderState) => {
const { component } = this.state;
if (!isEqual(nextProps, this.props)) {
this.setRenderComponent(nextProps);
}
if (nextState.component !== component) return true;
return false;
};
// set render Component : ok or error
setRenderComponent(props: PromiseRenderProps<T, K>) {
const ok = this.checkIsInstantiation(props.ok);
const error = this.checkIsInstantiation(props.error);
props.promise
.then(() => {
this.setState({
component: ok,
});
return true;
})
.catch(() => {
this.setState({
component: error,
});
});
}
// Determine whether the incoming component has been instantiated
// AuthorizedRoute is already instantiated
// Authorized render is already instantiated, children is no instantiated
// Secured is not instantiated
checkIsInstantiation = (
target: React.ReactNode | React.ComponentClass,
): React.FunctionComponent => {
if (isComponentClass(target)) {
const Target = target as React.ComponentClass;
return (props: any) => <Target {...props} />;
}
if (React.isValidElement(target)) {
return (props: any) => React.cloneElement(target, props);
}
return () => target as React.ReactNode & null;
};
render() {
const { component: Component } = this.state;
const { ok, error, promise, ...rest } = this.props;
return Component ? (
<Component {...rest} />
) : (
<div
style={{
width: '100%',
height: '100%',
margin: 'auto',
paddingTop: 50,
textAlign: 'center',
}}
>
<Spin size="large" />
</div>
);
}
}
常用命令
npm start#
运行这个脚本会启动服务,自动打开默认浏览器展示你的页面。当你重新编辑代码后,页面还会自动刷新。
npm run build#
运行这个脚本将会编译你的项目,你可以在项目中的 dist 目录中找到编译后的文件用于部署。
编译之后的文件经过压缩。如果你想知道更详细的信息可以查阅 编译。
如果你需要部署,可以查阅 部署。
image
npm run analyze#
analyze 脚本做的事情与 build 的相同,但是他会打开一个页面来展示你的依赖信息。如果需要优化性能和包大小,你需要它。
npm run lint#
我们提供了一系列的 lint 脚本,包括 TypeScript,less,css,md 文件。你可以通过这个脚本来查看你的代码有哪些问题。在 commit 中我们自动运行相关 lint。
npm run lint:fix#
这个脚本会自动修复一些 lint 错误,如果你被 lint 搞的焦头烂额,试试它吧。
npm test#
这个脚本会执行一系列测试,包括 e2e 测试。详细信息可以看 测试。
npm run ui npm run fetch:blocks#
这个脚本可以将所有的区块下载到当前项目中。你会得到与 https://preview.pro.ant.design/ 相同的界面。
如果速度缓慢,可以尝试升级 umi 版本到最新,并在 config.ts 设置 block.defaultGitUrl 为 'https://gitee.com/ant-design/pro-blocks'。
npm run i18n-remove#
常用链接
官网 ant-design-pro 12 步 30 分钟,完成用户管理的 CURD 应用 (react+dva+antd) dva-knowledgemap roadhog dva dva-cli dva 基础流程 api Concepts Redux 中文文档 antd-admin react-antd-admin1 react-antd-admin2 github-stars 编写 React 组件的最佳实践 部署 dva roadhog json-server roadhog-proxy JS函数式编程指南 js注释规范 model的继承和覆盖 React-Redux React Native 中 component 生命周期 React Native 中 component 生命周期 表单相关的几个问题(表单拆分、展示->可编辑状态的切换、Form.Item中的受控组件)
ant-design-pro
- model放在../models目录
- model在src\common\router.js中使用
'/ling/builder/model': { component: dynamicWrapper(app, ['entity'], () => import('../ling/builder/model/routes/EntityIndex')), },
- connet和loading
@connect(({ project, activities, chart, loading }) => ({
project,
activities,
chart,
projectLoading: loading.effects['project/fetchNotice'],
activitiesLoading: loading.effects['activities/fetchList'],
}))
- 获取数据
render() {
const {entity: {data}, loading} = this.props;
...
初始项目
npm i dva-cli -g dva -v
然后创建应用:
dva new user-dashboard cd user-dashboard cnpm i antd --save cnpm i babel-plugin-import --save-dev cnpm i moment --save-dev cnpm i jsonp --save-dev cnpm i axios --save-dev
组件
编码规范
React
props 一般用于父组件向子组件通信,在组件之间通信使用。
state 一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等。
纯函数组件是和状态无关的,所需数据都来自父级组件
class App extends Component {
constructor(){
this.state = {
height: 200,
plotCfg: {
margin: [60, 70, 40, 10],
},
}
}
render() {
return <Chart {...this.state}/>
}
}
const Chart = (props) => {
const {height} = props
return (
<ECharts height ={height} />
);
}
使用bindActionCreators整合多个model
如何在bindActionCreators中引入多个actionCreators模块
https://github.com/sorrycc/github-stars/blob/master/src/actions/index.js
https://github.com/sorrycc/github-stars/blob/master/src/models/user.js
import { createAction } from 'redux-actions'; export const userLogin = createAction('user/login');
import * as _actions from '../actions/index';
function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(_actions, dispatch), }; }
ref
组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM (virtual DOM)。只有当它插入文档以后,才会变成真实的 DOM 。根据 React 的设计,所有的 DOM 变动,都先在虚拟 DOM 上发生,然后再将实际发生变动的部分,反映在真实 DOM上,这种算法叫做 DOM diff ,它可以极大提高网页的性能表现。 但是,有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性(查看 demo07 )。
推荐写法
selectModal=null;
render() {
return (
<div>
<BaseIndex
{...this.indexProps}
/>
<Modal
width={'95%'}
maskClosable={false}
visible={this.state.selectTemplateVisible}
onOk={
() => {
this.setState({
selectTemplateVisible: false
})
let selections=this.selectModal.getSelectRows()
console.log(selections)
this.setState({currentSelection:selections });
}
}
onCancel={() => {
this.setState({
selectTemplateVisible: false
})
}}
>
<BaseIndex
{...this.selectTemplateProps}
ref={
(select)=>{
this.selectModal=select;
}
}
/>
</Modal>
</div>
)
}
老式写法
<!DOCTYPE html>
<html>
<head>
<script src="../build/react.js"></script>
<script src="../build/react-dom.js"></script>
<script src="../build/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
var MyComponent = React.createClass({
handleClick: function() {
this.refs.myTextInput.focus();
},
render: function() {
return (
<div>
<input type="text" ref="myTextInput" />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
});
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
</script>
</body>
</html>
上面代码中,组件 MyComponent 的子节点有一个文本输入框,用于获取用户的输入。这时就必须获取真实的 DOM 节点,虚拟 DOM 是拿不到用户输入的。为了做到这一点,文本输入框必须有一个 ref 属性,然后 this.refs.[refName] 就会返回这个真实的 DOM 节点。 需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。上面代码中,通过为组件指定 Click 事件的回调函数,确保了只有等到真实 DOM 发生 Click 事件之后,才会读取 this.refs.[refName] 属性。 React 组件支持很多事件,除了 Click 事件以外,还有 KeyDown 、Copy、Scroll 等,完整的事件清单请查看官方文档。
Supported Events
https://facebook.github.io/react/docs/events.html#supported-events
[name]: value
handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name;
this.setState({ [name]: value }); }
React.PropTypes
MyComponent.propTypes = {
// You can declare that a prop is a specific JS primitive. By default, these
// are all optional.
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
optionalSymbol: React.PropTypes.symbol,
// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
optionalNode: React.PropTypes.node,
// A React element.
optionalElement: React.PropTypes.element,
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
optionalMessage: React.PropTypes.instanceOf(Message),
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// An object that could be one of many types
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// An array of a certain type
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// An object with property values of a certain type
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// An object taking on a particular shape
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// You can chain any of the above with `isRequired` to make sure a warning
// is shown if the prop isn't provided.
requiredFunc: React.PropTypes.func.isRequired,
// A value of any data type
requiredAny: React.PropTypes.any.isRequired,
// You can also specify a custom validator. It should return an Error
// object if the validation fails. Don't `console.warn` or throw, as this
// won't work inside `oneOfType`.
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// You can also supply a custom validator to `arrayOf` and `objectOf`.
// It should return an Error object if the validation fails. The validator
// will be called for each key in the array or object. The first two
// arguments of the validator are the array or object itself, and the
// current item's key.
customArrayProp: React.PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
Redux
https://zhuanlan.zhihu.com/p/22405838
学习函数式编程,必须掌握很多术语,否则根本看不懂文档。Reduce 和 Transduce 的含义
界面 => action => reducer => store => react => virtual dom => 界面
一种编码规范或者设计模式,或者一种约定
Effects
- put 用于触发 action 。
yield put({ type: 'todos/add', payload: 'Learn Dva' });
- call 用于调用异步逻辑,支持 promise 。
const result = yield call(fetch, '/todos');
- select 用于从 state 里获取数据。
const todos = yield select(state => state.todos);
- yield 把异步逻辑通过同步的方式组织起来。
- async用来申明里面包裹的内容可以进行同步的方式执行,
- await await则是进行执行顺序控制,每次执行一个await,程序都会暂停等待await返回值,然后再执行之后的await
- await只能用在async函数之中,用在普通函数中会报错
- await和yield都是表示暂停,外面包裹一层async表示里面的代码可以采用同步的方式进行处理
export async function fetchUser(username, password) {
return await fetch('https://api.github.com/user', auth({}, username, password))
.then(res => res.json());
}
effects: {
*login(action, { put }) {
yield put({
type: 'login/start',
});
const { username, password } = action.payload;
const userInfo = yield GithubAPI.fetchUser(username, password);
if (userInfo.message) {
yield put({
type: 'login/error',
payload: userInfo.message,
});
} else {
yield put({
type: 'login/success',
payload: {username, password, userInfo},
});
yield put({
type: 'stars/sync',
});
}
yield put({
type: 'login/end',
});
},
},
export async function fetchStars(url, username, password) {
console.log('fetchStars', url);
let links;
const result = await fetch(url, auth({type: 'json'}, username, password)).then(res => {
links = parseLink(res.headers.get('Link'));
return res.json();
});
return {
result: result.map(selectStar),
links,
};
}
React-Redux
- 实际项目中,你应该权衡一下,是直接使用 Redux,还是使用 React-Redux。后者虽然提供了便利,但是需要掌握额外的 API,并且要遵守它的组件拆分规范
- React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)
一、UI 组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 没有状态(即不使用this.state这个变量)
- 所有数据都由参数(this.props)提供
- 不使用任何 Redux 的 API
二、容器组件
- 负责管理数据和业务逻辑,不负责 UI 的呈现
- 带有内部状态
- 使用 Redux 的 API
分工
UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。 你可能会问,如果一个组件既有 UI 又有业务逻辑,那怎么办?回答是,将它拆分成下面的结构:外面是一个容器组件,里面包了一个UI 组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。 React-Redux 规定,所有的 UI 组件都由用户提供,容器组件则是由 React-Redux 自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它
connect()
const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps)(TodoList)
四、mapStateToProps()
- 输入逻辑:将state映射到 UI 组件的参数(props),
- 是一个函数它的作用就是像它的名字那样,建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系
- 作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射
- mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
- mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。
- 使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
- connect方法可以省略mapStateToProps参数,那样的话,UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
const mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
}
}
五、mapDispatchToProps()
- 输出逻辑:将用户对 UI 组件的操作映射成 Action
- 它定义了哪些用户的操作应该当作 Action,传给 Store。
- 它可以是一个函数,也可以是一个对象。
- mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数
const mapDispatchToProps = (
dispatch,
ownProps
) => {
return {
onClick: () => {
dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: ownProps.filter
});
}
};
}
- 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出
const mapDispatchToProps = {
onClick: (filter) => {
type: 'SET_VISIBILITY_FILTER',
filter: filter
};
}
六、<Provider> 组件
- 全局 state React-Router 路由库有相同功能,github-star项目的全局actions也有这个功能
下面代码中,Provider在根组件外面包了一层,这样一来,App的所有子组件就默认都可以拿到state了 store放在了上下文对象context上面。然后,子组件就可以从context拿到store
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp);
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
class VisibleTodoList extends Component {
componentDidMount() {
const { store } = this.context;
this.unsubscribe = store.subscribe(() =>
this.forceUpdate()
);
}
render() {
const props = this.props;
const { store } = this.context;
const state = store.getState();
// ...
}
}
VisibleTodoList.contextTypes = {
store: React.PropTypes.object
}
const Root = ({ store }) => (
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
);
roadhog
roadhog 是一个 cli 工具,提供 server、 build 和 test 三个命令,分别用于本地调试和构建。命令行体验和 create-react-app 一致,配置略有不同,比如默认开启 css modules,然后还提供了 JSON 格式的配置方式。
由于 create-react-app 的默认配置不能满足需求,而他又不提供定制的功能,于是基于他实现了一个可配置版。所以如果既要 create-react-app 的优雅体验,又想定制配置,那么可以试试 roadhog
build:dll
- .roadhogrc.js中新增
dllPlugin: { exclude: ["babel-runtime", "roadhog", "cross-env"], include: ["dva/router", "dva/saga", "dva/fetch"] }
- package.json 中scripts配置
"scripts": { "start": "roadhog server", "dev": "cross-env BROWSER=none HOST=0.0.0.0 roadhog server", "build": "roadhog build", "precommit": "npm run lint", "lint": "eslint --fix --ext .js src", "build:dll": "roadhog buildDll", "build:new": "node version && roadhog build" },
- 运行
npm run build:dll
dva
dva 是一个基于 react 和 redux 的轻量应用框架,概念来自 elm,支持 side effects、热替换、动态加载、react-native、SSR 等,已在生产环境广泛应用
等价写法
export default connect(({app, loading}) => ({app, loading}))(function(props) {
return (
<div className={styles.normal}>
{console.log(props)}
</div>
);
});
===
// const IndexPage = (props) => {
// const {children, location, dispatch, app, loading}=props
// return (
// <div className={styles.normal}>
// {console.log(props)}
// {console.log(app)}
// </div>
// );
// }
const IndexPage = ({children, location, dispatch, app, loading,routes}) => {
return (
<div className={styles.normal}>
{console.log(routes)}
</div>
);
}
IndexPage.propTypes = {};
export default connect(({app, loading}) => ({app, loading}))(IndexPage)
dva cil
CLI for dva .
dva-cli use roadhog for build and server, view roadhog#Configuration (中文版) for details.
安装dva
npm i dva-cli -g
新建app
dva new user-dashboard cd user-dashboard
添加antd和babel
npm i antd --save npm i babel-plugin-import --save-dev
npm i dva-loading --save
修改 src/index.js 加载插件,在合适的地方加入下面两句
import createLoading from 'dva-loading'; app.use(createLoading());
新建功能命令
dva g route product-list dva g model products dva g component Editor dva g component Users/UserModal dva g component Header --no-css
组件
定义组件
components\users\list.js
function list ({
loading,
dataSource,
pagination,
onPageChange,
onDeleteItem,
onEditItem
}) {
const columns = [
........
]
return (
<div>
<Table
className={styles.table}
bordered
scroll={{ x: 1200 }}
columns={columns}
dataSource={dataSource}
loading={loading}
onChange={onPageChange}
pagination={pagination}
simple
rowKey={record => record.id}
/>
</div>
)
}
list.propTypes = {
onPageChange: PropTypes.func,
onDeleteItem: PropTypes.func,
onEditItem: PropTypes.func,
dataSource: PropTypes.array,
loading: PropTypes.any,
pagination: PropTypes.any
}
export default list
路由-使用组件
- import UserList from '../components/users/list'
- 定义参数
const userListProps = {
dataSource: list,
loading,
pagination: pagination,
onPageChange (page) {
const query = location.query
dispatch(routerRedux.push({
pathname: '/users',
query: {
...query,
page: page.current,
pageSize: page.pageSize
}
}))
},
onDeleteItem (id) {
dispatch({
type: 'users/delete',
payload: id
})
},
onEditItem (item) {
dispatch({
type: 'users/showModal',
payload: {
modalType: 'update',
currentItem: item
}
})
}
}
路由-使用组件
...
return (
<div className='content-inner'>
<UserSearch {...userSearchProps} />
<UserList {...userListProps} />
<UserModalGen />
</div>
)
...
路由-构造函数和connect
function Users ({ location, dispatch, users }) {//users 为 model
const { loading, list, pagination, currentItem, modalVisible, modalType } = users// 取 model中的值绑定到页面
const { field, keyword } = location.query
...
Users.propTypes = {
users: PropTypes.object,
location: PropTypes.object,
dispatch: PropTypes.func
}
function mapStateToProps ({ users }) {
return { users }
}
export default connect(mapStateToProps)(Users)
组件属性获取与传递/设置
- 设置参数
const demoSearchProps = {
record:{},
onSearch (params) {
dispatch({
type: `demo/Search`,
payload: params
})
},
onReset () {
dispatch({
type: 'demo/Reset'
})
},
beforeSearch(params){
console.log(params);
}
}
- 传递参数,不管哪种获取参数类型,使用方式都一致
<SearchTemplate {...demoSearchProps} /> <DemoList {...demoListProps} />
- 获取参数
- 通过props获取参数,适用于class SearchTemplateBase extends Component类型 const {beforeSearch, onSearch, afterSearch} = this.props;
handleSearch = (e) => {
const {beforeSearch, onSearch, afterSearch} = this.props;
this.props.form.validateFields((err, values) => {
if (!err) {
if (beforeSearch) {
if (beforeSearch(values)) {
// 还是要交给上层组件处理, 因为要触发table组件的状态变化...
if (onSearch) {
if (onSearch(values) && afterSearch) {
afterSearch(values);
}
}
}
}
}
});
};
- 获取参数 在构造函数中获取 适用于非class类型 export default list
function list ({ loading, dataSource, pagination, onPageChange, onDeleteItem, onEditItem, isMotion, location }) {
const handleMenuClick = (record, e) => {
if (e.key === '1') {
onEditItem(record)
} else if (e.key === '2') {
confirm({
title: '您确定要删除这条记录吗?',
onOk () {
onDeleteItem(record.id)
}
})
}
}
子组件与父组件
常用代码
合并对象
let tableConfig; try { const tmp = require(`../../schema/${tableName}.config.js`); // 个性化配置加载失败也没关系 tableConfig = Object.assign({}, globalConfig.DBTable.default, tmp); // 注意合并默认配置 } catch (e) { logger.warn('can not find config for table %s, use default instead', tableName); tableConfig = Object.assign({}, globalConfig.DBTable.default); }
类型判断与异常
let ids_ = ; if (value instanceof Array) { ids_ = keys.join(','); } else if (value instanceof String) { ids_ = keys } else { throw new Error("不支持的主键类型"); }
条件渲染
{ this.state.current===0 && <InnerPage {...this.programProps} > </InnerPage> }
{tableConfig.showExport ? <Button onClick={this.handleExport}><Icon type="export"/>导出</Button> : } {tableConfig.showImport ? <Upload {...uploadProps}><Button><Icon type="upload"/>导入</Button></Upload> : }
table filter
{
title: '代码名称',
dataIndex: 'className',
filters: [
{
text: status[0],
value: 0,
},
{
text: status[1],
value: 1,
},
{
text: status[2],
value: 2,
},
{
text: status[3],
value: 3,
},
],
render(val) {
return <Badge status={statusMap[val]} text={status[val]}/>;
},
}
日期显示
render: val => {moment(val).format('YYYY-MM-DD HH:mm:ss')},
table不可勾选
const rowSelection = {
selectedRowKeys,
onChange: this.handleRowSelectChange,
getCheckboxProps: record => ({
disabled: record.disabled,
}),
};
Divider
<Divider style=模板:Margin: '40px 0 24px'/>
基础知识
合并DBTable
部署
nginx
server
{
listen 666;
server_name 47.92.30.98;
root /home/www/antd-admin/dist;
location /api {
proxy_pass http://localhost:8000/api;
}
location / {
index index.html;
try_files $uri $uri/ /index.html;
}
}
apache
.htaccess RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.html [QSA,L]
常见问题
浏览器路径问题
// 1. Initialize
const app = dva({
// ...createLoading({
// effects: true,
// }),
// history: browserHistory,
onError (error) {
message.error(error.message)
},
})
中注释掉history: browserHistory, 路径中就带#,可以直接浏览器运行
antd没有样式
babel-plugin-import 用于按需引入 antd 的 JavaScript 和 CSS,这样打包出来的文件不至于太大。
npm i antd --save npm i babel-plugin-import --save-dev
修改 .roadhogrc.js,在 "extraBabelPlugins" 里加上:
["import", { "libraryName": "antd", "style": "css" }]
const path = require('path')
export default {
entry: 'src/index.js',
// "theme": "./theme.config.js",
// 接口代理示例
"proxy": {
},
"env": {
"development": {
"extraBabelPlugins": [
"dva-hmr",
"transform-runtime",
["import", { "libraryName": "antd", "style": true }]
]
},
"production": {
"extraBabelPlugins": [
"transform-runtime",
["import", { "libraryName": "antd", "style": true}]
]
}
}
}
TypeScript
// tsconfig.json
{ "compilerOptions": { "moduleResolution": "node", "jsx": "preserve", "allowSyntheticDefaultImports": true } }
setState后获取到的thisstate没变,还是初始state
this.setState 是在 render 时, state 才会改变调用的, 也就是说, setState 是异步的. 组件在还没有渲染之前, this.setState 还没有被调用.这么做的目的是为了提升性能, 在批量执行 State 转变时让 DOM 渲染更快.
void setState( function|object nextState, [function callback] )
所以正确做法是
this.setState( Object.assign({}, { data }), () => console.log(this.state) )
正确显示头行结构的面包屑
- id: string, 唯一id
- bpid: string, 面包屑导航的父id
- mpid: string, 菜单的父id,缺省时为一级菜单,为-1时在菜单中不显示
- name: 显示名称
- route: 匹配路由,缺省时不做跳转
- icon: 在名称前显示的图标
{
id: i,
name: 'project',
router: '/project',
},
{
id: i+1,
mpid: -1,
bpid: i,
name: 'project Detail',
router: '/project/:id',
},
,{
path: 'project',
getComponent (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./builder/models/Project'))
cb(null, require('./builder/pages/ProjectIndex'))
}, 'project')
}
}, {
path: 'project/:id',
getComponent (nextState, cb) {
require.ensure([], require => {
registerModel(app, require('./builder/models/ProjectDetail'))
cb(null, require('./builder/pages/ProjectDetail'))
}, 'project-detail')
}
}
Modal内容不清空
<Modal /> 组件有标准的 React 生命周期,关闭后状态不会自动清空。 如果希望每次打开都是新内容,需要自行手动清空旧的状态。或者打开时给 Modal 设置一个全新的 key, React 会渲染出一个全新的对话框 <Modal key={this.state.newKey} visible={this.state.visible} />
modal有时显示,有时不显示
2个modal,使用了相同的key,导致有时候显示有时候不显示,相互影响
table datasouce明明有多条数据却只显示一条
设置的rowKey={record => record.id}当id都为空时只显示出一条
.roadhogrc中的配置和router.js中的配置冲突
Failed to execute 'pushState' on 'History' error when using window.history.pushState function
history.pushState(data, null, newUrl);==> history.pushState(null, null, newUrl);
data的值过大