Understanding the Three-Layer Architecture
React, a leading JavaScript framework, forms the foundation of modern web applications. In this project, we leverage additional tools to streamline development: Dva for state management, Antd for UI components, and Umi as a comprehensive framework solution.
Core Technologies
- React: A JavaScript library for building user interfaces
- Dva: A lightweight front-end framework based on Redux and React, providing simplified state management
- Antd: An enterprise-class UI design language and React UI library
- Umi: A comprehensive enterprise-level front-end application framework
Project Architecture
The application follows a three-layer architecture:
- Models: Handle application state and business logic
- Services: Manage API requests and data fetching
- Components: Present the UI and user interactions
Services Layer
The services layer contains functions that communicate with backend APIs. These functions typically return Promise objects and handle HTTP requests.
export function fetchData (requestData) {
return request({
method: "post",
url: `${apiEndpoint}/data`,
data: JSON.stringify(requestData),
})
}
The underlying request function is a wrapper around axios:
import axios from "axios"
export default async function request (options) {
let response
try {
response = await axios(options)
return response
} catch (err) {
return response
}
}
Models Layer
Models define the application state and how it can be modified. They contain namespace, state, reducers, effects, and subscriptions.
export default {
namespace: "appData", // Namespace is required
state: { count: 0 }, // Initial state values
// Reducers handle state changes
reducers: {
incrementCount(state, { payload: { newCount }}) {
return { ...state, count: newCount };
},
},
// Effects handle asynchronous operations and business logic
effects: {
*fetchData({ payload: { id } }, { call, put, select }) {
const { data } = yield call(apiService.fetchData, { itemId: id });
yield put({
type: "incrementCount",
payload: {
newCount: data.value,
},
});
},
*loadUserInfo(_,{call,put}) {
// Additional effect logic
}
},
subscriptions: {
// Listen to router changes and trigger actions
setup ({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname==="/dashboard") {
dispatch({ type: "loadUserInfo" });
}
});
}
}
}
Components Layer
Components handle user interactions and dispatch actions to the models layer.
handleButtonClick = () => {
dispatch({
type: "appData/fetchData",
payload: {
id: this.state.currentId,
},
})
}
Data Flow Overview
The complete data flow follows this sequence:
- User interaction triggers a component handler
- The handler dispatches an action to the models layer
- The models layer effects process the action, potentially making API calls
- API responses trigger reducer actions to update state
- State changes trigger component re-rendering
Component State Connection
To connect components with the models state, use the connect function from dva:
import { Component } from "react"
import { connect } from "dva"
class Dashboard extends Component {
handleButtonClick = () => {
// Component implementation
}
render () {
const { count } = this.props;
return (
<div>
<button onClick={this.handleButtonClick}>Fetch Data</button>
<p>Current count: {count}</p>
</div>
)
}
}
function mapStateToProps (state) {
const { count } = state.appData;
return {
count,
};
}
export default connect(mapStateToProps)(Dashboard)
Best Practices for Rendering
Components re-render when state or props change. To optimize performance:
- Keep render methods lightweight, focusing on UI presentation
- Avoid complex data processing in render methods
- Handle data transformations in the models layer
- Pass processed data rather than raw data to child components