React 组件
两种常用的定义方式
- Stateless Functional Component (pure function)
function App(props) {
function handleClick() {
props.dispatch({ type: 'app/create' });
}
return <div onClick={handleClick}>{props.name}</div>
}
- Class
class App extends React.Component {
handleClick() {
this.props.dispatch({ type: 'app/create' });
}
render() {
return <div onClick={this.handleClick.bind(this)}>{this.props.name}</div>
}
}
React生命周期
JSX语法
- 组件嵌套
组件名称首字母必须大写
<App>
<Header />
<MainContent />
<Footer />
</App>
- className
<div className='myClass'>
...
</div>
- 内联样式添加
<div style={{ height:'20px', color:'#000000', fontSize:'16px' }}>
...
</div>
- Javascript 表达式
<span> { this.props.text } </span>
<span> { this.props.text ? this.props.text : '' } </span>
<div>
{
this.props.todos.map((todo, i) => <li key={i}>{todo}</li>)
}
</div>
React基本的父子组件传值方式
class ParentComponent extends React.Component {
render() {
return(
<div>
<span>some text...</span>
<SonComponent name="myName"/>
</div>
)
}
function SonComponent(props) {
return <div>{props.name}</div>
}
Router
- 项目目录中的router.js 返回一个组件并且export
function RouterConfig({ history, app }) {
...
return (
<LocaleProvider locale={zhCN}>
<Router history={history}>
<Switch>
{/* <Route path="/user/login" component={Login} /> */}
<Route path="/" render={props => <BasicLayout {...props} {...passProps} />} />
</Switch>
</Router>
</LocaleProvider>
);
}
export default RouterConfig;
index.js(systemAdmin.js)
import dva from 'dva';
import { browserHistory } from 'dva/router';
import createLoading from 'dva-loading';
// 1. Initialize
const app = dva({
onError(e) {
if (e.handleError) {
e.handleError();
} else {
throw e;
}
},
history: browserHistory,
});
// 2. Plugins
app.use(createLoading());
// 3. Model
app.model(require('./models/newTenant'));
...
// 4. Router
app.router(require('./router'));
// 5. Start
app.start('#root');
Routes
- 大致结构
import ...
class MyComponent extends React.Component{
componentDidMount() {
...
}
render(){
return(
...
)
}
}
export default connect(({ mycomponent }) => ({
mycomponent,
}))(MyComponent);
- 使用装饰符形式的结构
import ...
@connect(({ mycomponent }) => ({
mycomponent,
}))
export default class MyComponent extends React.Component{
componentDidMount() {
...
}
render(){
return(
...
)
}
}
Model
- 大致结构
import ...
export default {
namespace:...,
state:{
// 状态
...
},
reducers:{
// 改变state的方法
...
},
effects:{
// 异步方法(多用于请求接口)
...
},
subscriptions:{
// 订阅
...
}
}
reducers
- 常用
// 直接覆盖state的某个直接子属性
overrideStateProps(state, { payload }) {
return {
...state,
...payload,
};
},
// 改变state的第二层子属性
updateStateProps(state, { payload }) {
const { name, value } = payload;
return {
...state,
...{ [name]: { ...state[name], ...value } },
};
},
- 使用方法举例
this.props.dispatch({
type: 'newTenant/overrideStateProps',
payload: {
checkTenantData: [],
},
});
this.props.dispatch({
type: 'machineList/updateStateProps',
payload: {
name: 'listParams',
value: {
search_text: 'custom text',
},
},
});
effects
* fetchLaunchDetail({ payload }, { call, put }) {
const fetchResult = yield call(
request,
api.fetchLaunchDetail(payload),
);
if (!fetchResult.XCmdrCode) {
yield put({
type: 'overrideStateProps',
payload: {
appLaunch: fetchResult.XCmdrResult,
},
});
} else {
Message.error('错误')
}
},
call
call用于调用异步逻辑,支持 promise 。
put
用于触发 action 。( 调用 reducer 或 effects )
select
用于从 state 里获取数据。
const todos = yield select(state => state.todos);
subscriptions
Subscriptions 是一种从 源 获取数据的方法,它来自于 elm。
Subscription 语义是订阅,用于订阅一个数据源,然后根据条件 dispatch 需要的 action。
数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。
- 监听history
setup({ dispatch, history }) {
history.listen((location) => {
const match = pathToRegexp('/map/machineMapInProject/:id').exec(location.pathname);
if (match) {
const id = match[1];
dispatch({
type: 'updateStateProps',
payload: {
name: 'listParams',
value: { project: id },
},
});
}
});
},
- 监听key事件
import key from 'keymaster';
...
app.model({
namespace: 'count',
subscriptions: {
keyEvent(dispatch) {
key('⌘+up, ctrl+up', () => { dispatch({type:'add'}) });
},
}
});
SaaS-Model && Services
将Dva中的 fetch 方法进行层层封装, 被model层调用进行网络请求, 然后将请求返回的值传回model层.
- 需要定义 请求路径(url) 和 请求参数(options)
以antdpro模板项目为例
static uploadExcel(params) {
return request({
url: `${apiHostMap.CM_HOST}/import/machines/excel`,
method: 'POST',
params: params,
headers: 'multipart/form-data',
});
}