# Migration from DVA
This guide helps you migrate applications from DVA to ModernX.
# Overview
ModernX provides a similar state management experience to DVA but with modern React patterns and better TypeScript support.
# Key Differences
| Feature | DVA | ModernX |
|---|---|---|
| API | dva-core | modernx-core |
| Hooks | connect() | useModel() |
| Effects | put/call | dispatch/async |
| Subscriptions | Built-in | Custom hooks |
| Loading | Built-in | Custom effects |
# Migration Steps
# 1. Install Dependencies
npm install modernx modernx-core
npm remove dva @types/dva
# 2. Model Migration
# DVA Model
// DVA model
export default {
namespace: 'user',
state: {
currentUser: null,
loading: false
},
reducers: {
saveCurrentUser(state, { payload }) {
return { ...state, currentUser: payload };
},
setLoading(state, { payload }) {
return { ...state, loading: payload };
}
},
effects: {
*login({ payload }, { call, put }) {
yield put({ type: 'setLoading', payload: true });
try {
const response = yield call(api.login, payload);
yield put({ type: 'saveCurrentUser', payload: response.data });
} catch (error) {
console.error('Login failed:', error);
} finally {
yield put({ type: 'setLoading', payload: false });
}
}
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/login') {
dispatch({ type: 'clearCurrentUser' });
}
});
}
}
};
# ModernX Model
// ModernX model
const userModel = {
name: 'user',
state: {
currentUser: null,
loading: false
},
reducers: {
setCurrentUser: (state, payload) => ({
...state,
currentUser: payload
}),
setLoading: (state, payload) => ({
...state,
loading: payload
}),
clearCurrentUser: (state) => ({
...state,
currentUser: null
})
},
effects: {
async login(payload) {
this.setLoading(true);
try {
const response = await api.login(payload);
this.setCurrentUser(response.data);
} catch (error) {
console.error('Login failed:', error);
throw error;
} finally {
this.setLoading(false);
}
}
}
};
# 3. Component Migration
# DVA Component
// DVA component
import { connect } from 'dva';
function UserComponent({ user, dispatch }) {
const handleLogin = (credentials) => {
dispatch({ type: 'user/login', payload: credentials });
};
return (
<div>
{user.loading ? (
<p>Loading...</p>
) : (
<button onClick={() => handleLogin({ email: 'test@example.com' })}>
Login
</button>
)}
{user.currentUser && <p>Welcome, {user.currentUser.name}</p>}
</div>
);
}
export default connect(({ user }) => ({ user }))(UserComponent);
# ModernX Component
// ModernX component
import { useModel } from 'modernx-core';
function UserComponent() {
const [userState, userDispatch] = useModel('user');
const handleLogin = (credentials) => {
userDispatch('login', credentials);
};
return (
<div>
{userState.loading ? (
<p>Loading...</p>
) : (
<button onClick={() => handleLogin({ email: 'test@example.com' })}>
Login
</button>
)}
{userState.currentUser && <p>Welcome, {userState.currentUser.name}</p>}
</div>
);
}
export default UserComponent;
# 4. App Setup Migration
# DVA Setup
// DVA setup
import dva from 'dva';
import createLoading from 'dva-loading';
const app = dva();
app.use(createLoading());
app.model(userModel);
app.model(postModel);
app.router(router);
app.start('#root');
# ModernX Setup
// ModernX setup
import { createApp } from 'modernx';
import { Provider } from 'modernx-core';
const app = createApp({
models: [userModel, postModel]
});
function App() {
return (
<Provider app={app}>
<Router />
</Provider>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
# 5. Subscriptions Migration
DVA subscriptions can be migrated to custom hooks:
# DVA Subscription
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/login') {
dispatch({ type: 'user/clearCurrentUser' });
}
});
}
}
# ModernX Custom Hook
// Custom hook for route-based actions
function useRouteActions() {
const [userDispatch] = useModel('user');
const location = useLocation();
React.useEffect(() => {
if (location.pathname === '/login') {
userDispatch('clearCurrentUser');
}
}, [location.pathname, userDispatch]);
}
// Use in component
function LoginComponent() {
useRouteActions();
// ... rest of component
}
# 6. Loading State Migration
# DVA Loading
// DVA provides built-in loading
function Component({ loading }) {
return (
<div>
{loading.effects['user/login'] && <p>Logging in...</p>}
</div>
);
}
export default connect(({ loading }) => ({ loading }))(Component);
# ModernX Loading
// ModernX requires custom loading state
const userModel = {
name: 'user',
state: {
loading: false,
loadingEffects: {}
},
reducers: {
setLoading: (state, payload) => ({
...state,
loading: payload
}),
setLoadingEffect: (state, { effect, loading }) => ({
...state,
loadingEffects: {
...state.loadingEffects,
[effect]: loading
}
})
},
effects: {
async login(payload) {
this.setLoadingEffect({ effect: 'login', loading: true });
try {
// Login logic
} finally {
this.setLoadingEffect({ effect: 'login', loading: false });
}
}
}
};
// Or create a loading utility
function createLoadingEffect(model) {
return {
...model,
state: {
...model.state,
loading: false
},
effects: {
...model.effects,
...Object.keys(model.effects || {}).reduce((acc, key) => {
acc[key] = async function(...args) {
this.setLoading(true);
try {
await model.effects[key].call(this, ...args);
} finally {
this.setLoading(false);
}
};
return acc;
}, {})
}
};
}
# Migration Checklist
- [ ] Replace
dvawithmodernxandmodernx-core - [ ] Convert model namespace to name
- [ ] Update reducers to use function syntax
- [ ] Convert effects to async/await
- [ ] Replace
connect()withuseModel()hook - [ ] Migrate subscriptions to custom hooks
- [ ] Update loading state handling
- [ ] Test all functionality
- [ ] Update TypeScript types if applicable
# Common Issues
# 1. Action Dispatching
// DVA
dispatch({ type: 'user/login', payload: credentials });
// ModernX
userDispatch('login', credentials);
# 2. State Access
// DVA
const { user } = this.props;
// ModernX
const [userState] = useModel('user');
# 3. Effects Error Handling
// DVA
try {
yield call(api.login, payload);
} catch (error) {
yield put({ type: 'setError', payload: error.message });
}
// ModernX
try {
await api.login(payload);
} catch (error) {
this.setError(error.message);
}
# Benefits of Migration
- Modern React: Uses hooks instead of HOCs
- Better TypeScript: Improved type safety
- Simpler API: More intuitive API design
- Better Performance: Optimized re-rendering
- Easier Testing: Better testability
- Future Proof: Actively maintained
# Next Steps
- Start with a single model
- Test thoroughly
- Gradually migrate other models
- Update tests
- Update documentation
# Need Help?
- Check the ModernX Documentation
- View Examples
- Open an issue on GitHub (opens new window)