Reputation: 10738
Is it a good practice to use async/await directly in React component then store the result in the store ? For example:
class User extends Component {
render() {
return <div>{this.props.user.name}</div>
}
componentWillMount() {
this.getUser();
}
async getUser() {
try {
const user = await userAction.get();
this.props.storeUser(user);
} catch (err) {}
}
}
const state2props = (state) => ({
user: state.User.user
});
const dispatch2props = dispatch => ({
storeUser: (user) => dispatch(userReducer.store(user)),
});
export default connect(state2props, dispatch2props)(User);
It seems more flexible than the classic react/redux pattern.
Upvotes: 1
Views: 1204
Reputation: 2272
Yes, you can use async/await in react components. It's not a bad practice It's just architecture question.
There are lots of ways to implement async logic in applications. In small application you can implement async logic in react components. When your application grow up, you will get some issues like duplicated code (for example you want to fetch user in several react components), code composition and code splitting.
You can use redux-thunk https://github.com/gaearon/redux-thunk, redux-saga https://github.com/redux-saga/redux-saga, redux-logic https://github.com/jeffbski/redux-logic or any other solution.
Moreover you can create your own custom middleware such as:
const reactions = {};
export const addReactions = signals => {
reactions = { ...reactions, ...signals };
};
export default (signalMiddleware = ({ getState, dispatch }) => next => action => {
if (!action.signal) {
return next(action);
}
if (!reactions[action.signal]) {
throw new Error(`There is no handler for ${action.signal} signal`);
}
reactions[action.signal]({ getState, dispatch, payload: action.payload });
});
Such middleware allows you implement business logic into separate layer. For example:
import { addReactions } from './path/to/signalMiddleware';
// Describe your Actions for middleware:
const fetchUser = (id) => ({
signal: 'FETCH_USER',
payload: id
});
const anotherAction = () => ({
signal: 'SOME_ANOTHER_ACTION_WITH_USER',
});
// Describe your business logic using middleware:
addReactions({
FETCH_USER: async ({dispatch}, {id}) => {
const user = await fetcher.get(id);
dispatch({
type: 'RECEIVE_USER',
payload: user,
});
},
SOME_ANOTHER_ACTION_WITH_USER: () => {
// do some awesone job :)
}
})
So our react component could be:
class User extends Component {
render() {
return <div>{this.props.user.name}</div>
}
componentDidMount() {
this.props.dispatch(fetchUser(123));
}
}
export default connect(state2props, dispatch2props)(User);
Now you can divide your application architecture into 3 layer:
1) View — react-components
2) Business logic — your middleware
3) Data logic — your reducer
Between view and business layer we use specific actions with signal
field and without type
field.
Between business and data logic we use actions with type
field.
This architecture allows you to get a strict separation of layers. This architecture is useful in big applications.
In small application it's ok to use redux-thunk or write async logic in react-components.
Upvotes: 3