问题
I'm trying to figure out how to reduce the amount of boilerplate when using React, Redux and TypeScript together. It might be that you can't in this case, but wanted to see if anyone has ideas.
I currently have a component that dispatches an action that toggles a menu, alternating between showing and hiding it. To do this I've defined my class something like this (omitting code related to state, focusing on the dispatching of action):
import {Action, toggleMenu} from "../../actions/index";
interface IConnectedDispatch {
toggleMenu: (isActive: boolean) => Action;
}
class HeaderMenu extends React.Component<IOwnProps & IConnectedState & IConnectedDispatch, any> {
constructor(props: IOwnProps & IConnectedState & IConnectedDispatch) {
super(props);
this.toggleMenuState = this.toggleMenuState.bind(this);
}
public render() {
return (
<button className={buttonClass} onClick={this.props.toggleMenu(this.props.isActive)} type="button">
</button>
);
}
}
const mapDispatchToProps = (dispatch: redux.Dispatch<Store.All>): IConnectedDispatch => ({
toggleMenu: (isActive: boolean) => dispatch(toggleMenu(isActive))});
The action is defined as
export function toggleMenu(isActive: boolean): Dispatch<Action> {
return (dispatch: Dispatch<Action>) => {
dispatch({
isActive,
type: "TOGGLE_MENU",
});
};
}
It feels like it should be possible to reduce the amount of code required to accomplish my goal here. Being new to React, Redux and TypeScript I fail to see exactly how though. Specifically it feels very repetitive to write the action name, toggleMenu, over and over. For example twice in this part:
const mapDispatchToProps = (dispatch: redux.Dispatch<Store.All>): IConnectedDispatch => ({
toggleMenu: (isActive: boolean) => dispatch(toggleMenu(isActive))});
Any advice is appreciated!
回答1:
There is a shorter way. You can simplify a lot of your code.
Action - Original
export function toggleMenu(isActive: boolean): Dispatch<Action> {
return (dispatch: Dispatch<Action>) => {
dispatch({
isActive,
type: "TOGGLE_MENU",
});
};
}
Action - Reduced
export const toggleMenu = (isActive: boolean) => ({
isActive,
type: "TOGGLE_MENU"
})
Properties Interface - Original
interface IConnectedDispatch {
toggleMenu: (isActive: boolean) => Action;
}
Properties Interface - Reduced
import { toggleMenu } from "./actions"
interface IConnectedDispatch {
toggleMenu: typeof toggleMenu
}
MapDispatch - Original
const mapDispatchToProps = (dispatch: redux.Dispatch<Store.All>): IConnectedDispatch => ({
toggleMenu: (isActive: boolean) => dispatch(toggleMenu(isActive))});
MapDispatch - Reduced
const mapDispatchToProps = {
toggleMenu
};
I can recommend this library typescript-fsa. It helps reduce a lot of boilerplate created by actions, especially async ones.
回答2:
mapDispatchToProps will accept an object of action creators instead of a function and automatically bind all them all to dispatch.
From the docs:
If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a
dispatchcall so they may be invoked directly, will be merged into the component’s props
This allows you to rewrite it as:
const mapDispatchToProps = {
toggleMenu
};
Note: I'm not sure what you would need to type this to (if at all) in typescript.
来源:https://stackoverflow.com/questions/46055018/shorter-way-to-mapdispatchtoprops-using-react-redux-and-typescript