react-redux์ connect
๋ฅผ ํด๋์ค ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉํ๋ ค๊ณ ํ๋ฉด Type 'foo' is not assignable to type 'void'
ํ์์ ์ค๋ฅ ๋ฉ์์ง๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ์ด๋ฌํ ํ์ดํ์ ๋ฌธ์ ๋ TypeScript๊ฐ ClassDecorator
์ฅ์ํ๋ ๊ฒ์ ์๋ช
์ ๋ณ๊ฒฝํ๋ ๊ฒ์ ํ์ฉํ์ง ์๋ ๊ฒ ๊ฐ์ง๋ง ์ด๊ฒ์ด ํ์ฌ react-redux ํ์ดํ์ ๊ตฌํ์ด๋ฉฐ ์๋์ ์ผ๋ก ์ํ๋๊ธฐ ๋๋ฌธ์ react-redux๋ props์ ๋ํด ๋ค๋ฅธ ์๋ช
์ ๊ฐ์ง ๋ค๋ฅธ ์ธ์คํด์ค๋ฅผ ๋ฐํํฉ๋๋ค .
connect
๋ฅผ ํจ์๋ก ์ฌ์ฉํ๋ฉด ์์๋๋ก ์๋ํฉ๋๋ค. ํ์ ๋ฌธ์ ๋ฅผ ์ผ์ผํค๋ ๊ฒ์ ๋ฐ์ฝ๋ ์ดํฐ๋ก์์ ์ฌ์ฉ๋ฒ๋ฟ์
๋๋ค.
์ ์์ ํ์ํ์ง๋ง ์ ์๋ ์๋ฃจ์ ์ ํ์ฌ ํ์ดํ์ด ํํํ ์ ์๋ ๊ฒ์ ๋ํ ์๊ฒฉํ ๊ฐ์ ์ด์ด์ผ ํฉ๋๋ค. ์ฌ๊ฐํ ํ๊ท ๋ฐ ๋ถ๋ถ์ ์ผ๋ก ๋ฐ์ฝ๋ ์ดํฐ๊ฐ "์คํ์ "์ผ๋ก ๊ฐ์ฃผ๋๊ธฐ ๋๋ฌธ์ ํ์ค ๊ธฐ๋ฅ ์ฌ์ฉ์ ๋ถ์ฐจ์ ์ธ ๊ฒ์ด๋ผ๊ณ ์ฃผ์ฅํฉ๋๋ค.
https://github.com/Microsoft/TypeScript/issues/4881 ์ด ๋ฐ์ด๋ ๋์ ๋ฐ์ฝ๋ ์ดํฐ ์
๋ ฅ ๋ฌธ์ ์ ๋ํ ์์ ํ ์๋ฃจ์
์ด ๊ฐ๋ฅํ์ง ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ์ฆ, (์ฒซ ๋ฒ์งธ ํจ์ค) ๋ชจ๋ ์ข
๋ฅ์ React.ComponentClass
๋ฅผ ์ถ๋ ฅํ์ฌ ์ฝ๋๊ฐ ์ต์ํ ์ปดํ์ผ๋๋๋ก ํ ๋ค์ (๋ ๋ฒ์งธ ํจ์ค) ๋ชจ๋ props(์์ ๋ฐ ์ฐ๊ฒฐ), ๋๋ฌด ๊ด๋ํ ์ง๋ผ๋ ์ฝ๋ ์ ํ์ ์ ์ด๋ ์ผ๋ถ props๋ฅผ ํ์ธํฉ๋๋ค.
๋ด๊ฐ ์ง๊ธ ๊ฐ์ง๊ณ ์๋ ์ ์ผํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ connect
๋ฅผ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉํ์ง ์๋ ๊ฒ์
๋๋ค. ์ด๋ค ์ฌ๋๋ค์ ์คํ์ผ์ ๋ณด๊ธฐ ํํ๋ค๊ณ ์๊ฐํ ์๋ ์์ง๋ง ์ฌ๋ฐ๋ฅด๊ฒ ์ ํ ํ์ธํ๊ณ ๋ ๊ธธ์ง ์์ผ๋ฉฐ ๋ฐ์ฝ๋ ์ดํฐ๋ณด๋ค ๋ ๋ง์ ๊ณณ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋์ ์:
@connect(mapStateToProps, mapDispatchToProps)
class MyComponent extends React.Component<...> { ... }
(๋ค์๊ณผ ๊ฐ์ ๊ฒ์) ์ฌ์ฉํ๋ค:
class MyComponentImpl extends React.Component<...> { ... }
const MyComponent = connect(mapStateToProps, mapDispatchToProps)(MyComponentImpl);
https://github.com/DefinitelyTyped/DefinitelyTyped/issues/8787 ์ด ์ฌ๊ธฐ์์ ์๋ํ๋ฉฐ ๋๋๋ก ์ด ๋ฌธ์ ์ ํผ๋๋ฉ๋๋ค. ์ถ๋ ฅ ๊ตฌ์ฑ ์์์ ์ ์ ํ ์ ํ์ด ์๋์ง ํ์ธํ๊ธฐ ์ํด ์ ํ ํํธ ๋๋ ์บ์คํธ๊ฐ ํ์ํ ์๋ ์์ต๋๋ค.
ํธ์ง: https://github.com/Microsoft/TypeScript/pull/12114 ์ด ๊ฒฝ์ฐ์ ๋์์ด ๋ ์ ์์ต๋๋ค. ๋ฐ์ฝ๋ ์ดํฐ๋ ์์ฑ๋ ๊ตฌ์ฑ ์์์ ๋ํ ๋ชจ๋ ์ ํ์ ๋ฒ์ ์ ์ํ์ ์์ฑํ๋๋ก ์ ์ฌ์ ์ผ๋ก ์์ฑ๋ ์ ์์ต๋๋ค. ์ด๋ ์์ ํ ์ด์์ ์ด์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ณ์ํด์ prop nullity์ ๋ํด ๋ ์ ๊ทน์ ์ผ๋ก ๊ตฌํํ๋๋ก ํฉ๋๋ค.
๋๋ ์์ ์ ๋น์ทํ ๋ฌธ์ ๊ฐ ์์๊ณ ์ฐ๊ฒฐ ๊ธฐ๋ฅ์ ์ํด ๊ทธ๊ฒ์ ๋จ์ด ๋จ ๋ ธ์ต๋๋ค. ์ ํ์ ๋ฌด์ํ๊ณ ์ด์ ๋ฐ์ ์ ํ์ ์ฌ์ฉํ์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ PR์ ์ ์ถํ ์ ์์ต๋๊น?
๋ฌผ๋ก ์ด์ผ! ์ด๋ฌํ ์ ํ์ด ์๋ ์์ฑ๋ ์ดํ๋ก ๋ง์ ๊ฒ์ด ๋ณ๊ฒฝ๋์์ง๋ง, ์ฅ์์ ์ง์์ ๋ณ๊ฒฝํ์ง ์๊ณ ๋ ์ ์ฉํ ๊ฒ์ด ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์ฌ์ ํ โโํ์์ ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ด์ ์ ์ ํ ์์คํ ์ ๋ค๋ฅธ ๋ถ์ ์ ํจ์ ํด๊ฒฐํ ์ ์ด ์์ผ๋ฏ๋ก ์๋ํด ๋ณด์ญ์์ค.
์๋ง๋ ๋งคํ๋ ์ ํ์ด ์ผ๋ถ๋ฅผ ์ป์ ์ ์์ผ๋ฏ๋ก ์ถ๋ ฅ์ ์ต์ํ _์ผ๋ถ_ ์ ์ฉํ ์ ํ ์ ๋ณด๊ฐ ์์ต๋๊น?
์๋ก
export function myConnect(mapStateToProps, mapDispatchToProps) {
return function (target) {
return <any>connect(mapStateToProps, mapDispatchToProps)(target);
}
}
๋๋ ์ด๊ฒ์ yolo-typing์ ๋ํ ๋ง์ ์์ ์ด ์๋ค๊ณ ์๊ฐํฉ๋๋ค. ์ฌ๊ธฐ์ ๋์ด๋ ํด๊ฒฐ ๋ฐฉ๋ฒ๊ณผ #8787, ํนํ ํจ์ ํธ์ถ์ ๋ํ ํํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ด ๊ทธ๋ ๊ฒ ๋์์ง ์์ต๋๋ค(ํนํ ๊ตฌ์ฑ ์์ ํด๋์ค์ฉ IDE ํ ํ๋ฆฟ ์ฌ์ฉ). ๋ชจ๋ ์ ํ์ ๊ตฌ์ฑ ์์๋ฅผ ๊ฐ๋ ๊ฒ์ ์ฌํ ์ผ์ ๋๋ค. :( ํจ์ ํธ์ถ ๋์ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉํ๊ธฐ ์ํด ์๋ฌด ์ ๋ ฅ์ด๋ ์ฐ๊ฒฐํ๋ ๊ฒ์ ์ค์ ๋ก ์นดํธ๋ฅผ ๋ง ์์ ๋๋ ๊ฒ์ ๋๋ค.
@connect
์ง์ ์ฌ์ฉํ๋ ค๋ฉด(์ฌ์ฉ์ ์ง์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋์
ํ์ง ์๊ณ ) ๋ค์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฌ์ฉํฉ๋๋ค.
@(connect(mapStateToProps, mapDispatchToProps) as any)
class MyComponent extends React.Component<...> {...}
๊ทธ๋ฌ๋ ๋๋ ๋ชจ๋ ํ์ดํ์ด ์ค์ ๋ก ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ด ์๋๋ผ๋ @seansfkelley์ ๋ง์ ์ ์ ์ผ๋ก ๋์ํฉ๋๋ค...
๋ฐ์ฝ๋ ์ดํฐ ๊ตฌ๋ฌธ์ด ํ๋ฅญํจ์๋ ๋ถ๊ตฌํ๊ณ ํ์ค ๋ฐฉ์์ผ๋ก ์ฐ๊ฒฐ๊ณผ ๊ฐ์ HOC๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ๋์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
1) ๋ฐ์ฝ๋ ์ดํฐ ์ฌ์์ ์ด๊ธ๋๋ ํด๋์ค์ ์ ํ์ ์์ ํฉ๋๋ค.
2) Connect๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์๋๋ผ HOC์
๋๋ค.
3) stateless ๋ฐ stateful ๊ตฌ์ฑ ์์์ ๋ํด ๋ค๋ฅธ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ์ฝ๋์ ์ด์์ฑ์ ๊นจ๋จ๋ฆฝ๋๋ค.
๋ฐ๋ผ์ ๊ฐ๋ฐ์๊ฐ ์๋ชป๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์๋๋ก ์ ์๋ฅผ ์ ๊ณตํ์ง ๋ง์ญ์์ค.
@npirotte ๋์ํฉ๋๋ค. HOC๋ฅผ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฌ์ฉํ๋ ๊ฒ์ ๋ฐ์ฝ๋ ์ดํฐ ์ฌ์์ ์๋ฐํ๋ ๊ฒ์ ๋๋ค. ๋ ์ด์ ๊ธฐ๋ณธ ํด๋์ค๊ฐ ์๋๋ผ ์์ ํ ๋ค๋ฅธ ํด๋์ค์ ๋๋ค.
์ฌ์ฉ์ ์ง์ ๋ฐ์ฝ๋ ์ดํฐ์ ๋์ผํ ๋ฌธ์ ๊ฐ ์๊ณ TS์์ ์ง์ ์ํ๋ฅผ ๋ณด๊ณ ์์ต๋๋ค(์์ง ํ๋๊ทธ ๋ค์ ์๊ณ AFAICS์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํํ ๊ณํ์ด ์์ต๋๋ค). ์ง์์ ์ค๋จํ๋ ค๊ณ ํฉ๋๋ค. ๊ทธ๋ค์. ๋ํ ๋ฐ์ฝ๋ ์ดํฐ๋ ์ฌ์ ํ w3c ์ด์์ ๋๋ค.
๊ตฌ์ฑ ์์๋ฅผ ์ ๊ตฌ์ฑ ์์๋ก ๋ํํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๊ธฐ์กด ํด๋์ค๋ฅผ _์ฅ์_ํ๋ ค๊ณ ํ๋ ๋์ ๊ณ ์ฐจ ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค.
๊ฐ์ ธ์ค๊ธฐ๋ฅผ ํด๋ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ค์์ ์ ์๊ฒ ํจ๊ณผ์ ์ ๋๋ค.
const { connect } = require('react-redux');
@connect(mapStateToProps, mapDispatchToProps)
class MyComponent extends React.Component<...> { ... }
require
์
๋ ฅํ ๋ฐฉ๋ฒ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ @TriStarGod . ์ด ๊ฒฝ์ฐ connect
๋ฅผ any
๋ก ์
๋ ฅํ๊ฒ ๋ ๊ฒ ๊ฐ์ต๋๋ค.
๋ค๋ฅธ ์๋ฃจ์ /ํด๊ฒฐ ๋ฐฉ๋ฒ.
์ด๋ฏธ ์์ฒด ์ฑ ์ํ๊ฐ ์์ผ๋ฏ๋ก ๋ค์๊ณผ ๊ฐ์ ํํ์ ๋งค์ฐ ์งง์ ๊ธฐ๋ฅ์ด ํ์ํฉ๋๋ค.
export interface PageProps {
routing: RouterState;
}
export interface PageDispatch {
navigate: () => void
}
@connect<PageProps, PageDispatch>(
state => ({
routing: state.routing
}),
dispatch => ({
navigate: () => dispatch(push("/"))
})
)
export class Page extends React.Component<PageProps & PageDispatch> {
...
}
๋ค์์ ๋ํ๋ ์ฐ๊ฒฐ ๋ฐฉ๋ฒ์ ๋๋ค.
import { connect } from "react-redux";
import { ApplicationState } from './rootReducer';
interface MapPropsParam<TProps>{
(state: ApplicationState, ownProps?: TProps): TProps
}
interface MapDispatchParam<TProps, TDispatchProps>{
(dispatch: Redux.Dispatch<ApplicationState>, ownProps?: TProps): TDispatchProps;
}
export interface ConnectedComponent<TProps> {
<TComponent extends React.ComponentType<TProps>>(component: TComponent): TComponent;
}
function connectToAppState<TProps, TDispatchProps = {}>(mapProps: MapPropsParam<TProps>, mapDispatch?: MapDispatchParam<TProps, TDispatchProps>) : ConnectedComponent<TProps> {
return connect<TProps, TDispatchProps, TProps>(mapProps, mapDispatch) as ConnectedComponent<TProps & TDispatchProps>;
}
export {
connectToAppState as connect
}
๊ทํ์ ์์ ์ ๋ํ Thx. ๊ตฌ๋ ํ๊ณ ์ด ๋ฌธ์ ์ ๋ํ ์งํ์ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ต๋๋ค.
@offbeatful mapDispatchToProps ์ ๋ค๋ฅธ ๋ณํ์ ๋ํ ์ ํ ์ ์ธ์ ์ ๊ณตํ์ธ์.
๊ฐ์ฒด๊ฐ ์ ๋ฌ๋๋ฉด ๋ด๋ถ์ ๊ฐ ํจ์๋ Redux ์์ ์์ฑ์๋ก ๊ฐ์ฃผ๋ฉ๋๋ค. ํจ์ ์ด๋ฆ์ด ๊ฐ์ง๋ง ๋ชจ๋ ์์ ์์ฑ์๊ฐ ์ง์ ํธ์ถ๋ ์ ์๋๋ก ๋์คํจ์น ํธ์ถ๋ก ๋ํ๋ ๊ฐ์ฒด๋ ๊ตฌ์ฑ ์์์ ์ํ์ผ๋ก ๋ณํฉ๋ฉ๋๋ค.
@offbeatful ์ฃผ์์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ ๋ค๋ฅธ ํด๊ฒฐ ๋ฐฉ๋ฒ
myConnect.ts
import {
connect as originalConnect, MapDispatchToPropsParam, MapStateToPropsParam, MergeProps, Options
} from "react-redux";
import * as React from "react";
export interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
<TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>>(component: TComponent): TComponent;
}
interface MyConnect {
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps>,
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>;
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps>,
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
options?: Options<TStateProps, TOwnProps, TMergedProps>
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;
}
export const connect = originalConnect as MyConnect;
@pravdomil ์ค๋ํซ ๋ฐ ์ต์ ์ ํ(5.0.13)์ ๊ธฐ๋ฐ์ผ๋ก ์ ๋ฐ์ดํธ๋จ
import { ApplicationState } from "./rootReducer";
import * as React from "react";
import {
connect as originalConnect, MapDispatchToPropsParam, MapStateToPropsParam, MergeProps, Options
} from "react-redux";
export type InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> = <TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>>(component: TComponent) => TComponent;
interface MyConnect {
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, ApplicationState>,
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>;
<TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, ApplicationState>,
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
mergeProps?: MergeProps<TStateProps, TDispatchProps, TOwnProps, TMergedProps>,
options?: Options<TStateProps, TOwnProps, TMergedProps>
): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;
}
export const connect = originalConnect as MyConnect;
์๋ ํ์ธ์ @offbeatful (cc: @pravdomil ์ถ์ธก์ธ๊ฐ์?), ์ต๊ทผ์ ์ด ๋ฌธ์ ์ ๊ด๋ จ๋ ์คํ ์ค๋ฒํ๋ก ์ง๋ฌธ ์ ํ๋๋ฐ ์ด๊ฒ์ด ํ์ฌ ์ง์๋์ง ์๋๋ค๋ ๊ฒฐ๋ก ์ด ๋์์ต๋๋ค . ๊ทธ ์ง๋ฌธ์ ์ผ๋ถ๋ก ๋ด๊ฐ ์๋ํ ๊ฒ์ ๋ณด์ฌ์ค ์ ์ฅ์ ๋ฅผ ์ค๋นํ์ต๋๋ค. ์ด๊ฒ์ด ํ์ฌ ์ง์๋์ง ์๋๋ค๋ ๊ฒฐ๋ก ์ด ๋ฌ์ผ๋ฏ๋ก ์ค๋ ๊ทํ์ ์ฝ๋ ์ค๋ํซ์ ๋ณด๊ณ ๊ธฐ๋ปค๊ณ ์ด๋ฅผ ์ฌ์ฉํ๋๋ก ๋ด ์ ์ฅ์๋ฅผ ์ ๋ฐ์ดํธํ๋ ค๊ณ ํ์ต๋๋ค.
์ฌ๊ธฐ์์ ์ฝ๋ ์กฐ๊ฐ์ด ํตํฉ๋ ์ปค๋ฐ์ ๋ณผ ์ ์์ต๋๋ค.
์ด๊ฒ์ ๋ ์ด์ ์ด์ ์ ์ป์๋ ์ค๋ฅ๋ก ์๋ฆฌ ์ง๋ฅด์ง ์์ง๋ง ๊ตฌ์ฑ ์์๊ฐ ์ฐ๊ฒฐ๋์ด ์์ง๋ง ์์ฒด props๊ฐ ์๋ ์ฌ์ฉ ์ฌ๋ก์์ ์ด ์๋ก์ด ์๋ช
์ ์ด์ state props๋ ๋ค์๊ณผ ๊ฐ์ด ํ์ํ๋๋ก ๋ง๋ญ๋๋ค. ์์ ์ (TSX) ์ํ. ๋ด ์ ์ฅ์์ cd my-app && yarn start
๋ ๋ด๊ฐ ์๋ฏธํ๋ ๋ฐ๋ฅผ ๋ณด์ฌ์ค ๊ฒ์
๋๋ค.
์ด๊ฒ์ด ํด๊ฒฐ๋ ์ ์๊ฑฐ๋ ๋ถ๊ฐ๋ฅํ ์ผ์ด๋ผ๊ณ ์๊ฐํ์ญ๋๊น?
์ด connect
๋ฒ์ ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
connect(
mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, State>,
mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
mergeProps: null | undefined,
options: Options<State, TStateProps, TOwnProps>
): InferableComponentEnhancerWithProps<TStateProps & TDispatchProps, TOwnProps>
๋ง์ฝ ๊ทธ๋ ๋ค๋ฉด, ๋น์ ์ ๋จ์ง ํด๊ฒฐํ๋ ๋ฐ ํ์ํ options
๋งค๊ฐ ๋ณ์๊ฐ ๋ Options<any, any, any>
๋น์ ์ ํ์๊ฐ ์์ต๋๋ค ์ด๋ฐ ์์ผ๋ก TStateProps & TDispatchProps
๊ฐ ๋ ๊ฒ ์ฌ์ด ๋ฌธ์ ๋ฅผ TStateProps & any
๊ฐ๋จํ ๋งํด์:
const options = { withRef:true } as Options<any, any, any>;
export const Component = connect(mapStateToProps, mapDispatchToProps, null, options)
๋น์ ์ฒ๋ผ ๋ฉ์ง
@TomasHubelbauer ๊ธฐ๋ณธ redux ์ฐ๊ฒฐ ๋ฐฉ๋ฒ์ผ๋ก ๋์
์ด์ํ๊ฒ ์๋ํ ๊ฒ์
๋๋ค.
์ธ ๊ฐ์ง ์ ํ ๊ฐ์ ๊ณต์ ์ ํ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ต๋๋ค.
export type CounterStateProps = {
count: number;
};
export type CounterDispatchProps = {
onIncrement: () => void;
};
export type CounterOwnProps = {
initialCount: number;
};
export type CounterProps = CounterStateProps & CounterDispatchProps & CounterOwnProps;
๊ทธ๋ฐ ๋ค์ ์ํ ์ ์ฅ ๊ตฌ์ฑ ์์๋ฅผ ๊ตฌํํ์ญ์์ค.
export class StatefulCounter extends React.Component<CounterProps, CounterStateProps> {
timer: number;
componentDidMount() {
this.timer = setInterval(this.props.onIncrement, 5000);
}
componentWillUnmount() {
clearInterval(this.timer);
}
render() {
return (
<StatelessCounter count={this.props.count}/>
);
}
}
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉ์ ์ง์ ์ฐ๊ฒฐ ์ฝ๋๊ฐ ์๋ ์ฐ๊ฒฐ์์ redux์ ๋น๋๋ฅผ ์ฌ์ฉํ์ฌ ์ปค๋ฅํฐ ํด๋์ค๋ฅผ ์ด์ ๊ฐ์ด ๋ง๋ญ๋๋ค.
const mapStateToProps =
(state: RootState, ownProps: CounterOwnProps): CounterStateProps => ({
count: countersSelectors.getReduxCounter(state) + ownProps.initialCount,
});
const mapDispatchToProps =
(dispatch: Dispatch<CounterActionType>): CounterDispatchProps => bindActionCreators({
onIncrement: CounterActions.increment,
}, dispatch);
export const ConnectedCounter =
connect(mapStateToProps, mapDispatchToProps)(StatefulCounter);
@JohnHandley ์, ์ ๋ ์ด๊ฒ์ด ํจ๊ณผ๊ฐ ์๋ค๋ ๊ฒ์ ์๊ณ ์์ต๋๋ค . ์ ์ ์์๋ ๋ฐ์ฝ๋ ์ดํฐ๋ก ์๋ํ๋๋ก ํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ์ ์์ ์๋ํ๊ธฐ ์ ์ ๋ณด์ฌ์ฃผ์์ต๋๋ค. ๋น ๋ฐ์ฝ๋ ์ดํฐ ๋ณํ์ ์ฑ๊ณต์ ์ผ๋ก ์ฌ์ฉํ์ง๋ง ๋ฐ์ฝ๋ ์ดํฐ๋ ์๋ํ๊ฒ ๋ง๋ค๊ณ ์ถ์ต๋๋ค.
๋ํ, ๋๋ ๋น์ ์ด ์ฌ์ฉํ๋, ๋น์ ์ด ๋น์ ์ ์ ํ์ ํผํฉ ์๊ฐ CounterStateProps
์ ๋ฆฌํด ํ์
์ธ ( mapDispatchToProps
์ ๊ตฌ์ฑ ์์๋ก CounterProps
(๊ด์ฐฎ ์ธ์ด ๋์ ์ค๋ ์ํ๋ฅผ ๊ฐ์ง๊ณ props, Redux ๋์คํจ์น props ๋ฐ JSX ์์ฒด props)๋ฟ๋ง ์๋๋ผ ๊ตฌ์ฑ ์์ ์ํ์ ๋ํ ์ ํ์ผ๋ก๋ ์ฌ์ฉ๋ฉ๋๋ค.๋์ state
์๋ ์ธ๋ถ ๊ตฌ์ฑ ์์์ ์ ํ ์๋ช
๊ณผ ๊ด๋ จ์ด ์๋ ์์ฒด ์ ํ์ด ์์ด์ผ ํฉ๋๋ค.
์๋ฃจ์ ์ ์์ ํ ์ดํดํ์ง ๋ชปํ์ ์ ์์ผ๋ฏ๋ก TSX์์ Redux ์ํ ์ํ์ ์ง์ ํด์ผ ํ๋ค๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์๊ณ ๋ด ์ ์ฅ์(TSX์์ ์์ฒด props์ Redux ์ํ props์์ ๋ชจ๋ ์ฌ์ฉ)์์ ์ด ์์ ์ ์ํํ ์ ์๋ค๋ฉด , ๊ทธ๊ฒ๋ ์ข์๊ฑฐ์ผ!
์ด ๋ฌธ์ ์ ๋ํด +1
@offbeatful ์์ ์ค๋ํซ์ ์ ๋ฐ์ดํธํ์ผ๋ฉฐ ์ง๊ธ ์ฑ๊ณต์ ์ผ๋ก ์ฌ์ฉํ๊ณ ์์ต๋๋ค.
connect.ts ( IAppState
๋ redux ์ํ์ ์ธํฐํ์ด์ค์
๋๋ค)
import React from 'react'
import {
connect as originalConnect,
MapDispatchToPropsParam,
MapStateToPropsParam,
MergeProps,
Options,
} from 'react-redux'
export type InferableComponentEnhancerWithProps<IInjectedProps, INeedsProps> =
<IComponent extends React.ComponentType<IInjectedProps & INeedsProps>>(component: IComponent) => IComponent
export interface IConnect {
<IStateProps = {}, IDispatchProps = {}, IOwnProps = {}>(
mapStateToProps?: MapStateToPropsParam<IStateProps, IOwnProps, IAppState>,
mapDispatchToProps?: MapDispatchToPropsParam<IDispatchProps, IOwnProps>,
): InferableComponentEnhancerWithProps<IStateProps & IDispatchProps, IOwnProps>
<IStateProps = {}, IDispatchProps = {}, IOwnProps = {}, IMergedProps = {}>(
mapStateToProps?: MapStateToPropsParam<IStateProps, IOwnProps, IAppState>,
mapDispatchToProps?: MapDispatchToPropsParam<IDispatchProps, IOwnProps>,
mergeProps?: MergeProps<IStateProps, IDispatchProps, IOwnProps, IMergedProps>,
options?: Options<IStateProps, IOwnProps, IMergedProps>,
): InferableComponentEnhancerWithProps<IMergedProps, IOwnProps>
}
export const connect = originalConnect as IConnect
๊ทธ๋ฐ ๋ค์ ์ฐ๊ฒฐ๋ ๊ตฌ์ฑ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
import {connect} from 'path-to-my-connect/connect'
interface IOwnProps {
... props exposed for the real parent component
}
interface IStateProps {
... props from mapStateToProps
}
interface IDispatchProps {
... props from mapDispatchToProps
}
interface IProps extends IStateProps, IDispatchProps, IOwnProps {}
@connect<IStateProps, IDispatchProps, IOwnProps>(
(state: IAppState) => {
return {
foo: getFoo(state), // getFoo is a selector using 'reselect'
}
},
{setFoo, increment, decrement, ... your action creators},
)
class MyComponent extends React.PureComponent<IProps> {
// your component implementation
}
export default (MyComponent as any) as React.ComponentType<IOwnProps>
MyComponent as React.ComponentType<IOwnProps>
์ง์ ์บ์คํ
์ ์คํจํ๋ฏ๋ก ๋จผ์ any
๋ก ์
๋ ฅํ์ต๋๋ค. ๋๋ ๊ทธ๊ฒ์ด ํดํน์ด๋ผ๋ ๊ฒ์ ์๊ณ ์์ง๋ง ๋ถ๋ชจ์ ๊ตฌ์ฑ ์์ ์์ฒด์๋ ์ ์๋ํฉ๋๋ค.
์ด๊ฒ์ ๋ด๊ฐ ์๊ฐํด๋ผ ์ ์์๋ ๊ฐ์ฅ ์คํ ๊ฐ๋ฅํ์ง๋ง ์ฌ์ ํ ์ ํ์ ์ธ ์๋ฃจ์ ์ ๋๋ค.
"react-redux": "^5.0.6",
"typescript": "^2.8.1",
"@types/react-redux": "^6.0.0",
์ด๋ค ์์?
@ctretyak ์๋์ , ๊ฒฐ๊ตญ ์ง์ ์ ์ธ ํ์ผ์ ๋ง๋ค๊ณ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์์ ํด์ผ ํฉ๋๋ค. React Eco์ TS๋ ๊ฐ์ฅ ์นํ ์น๊ตฌ๊ฐ ์๋ ๊ฒ ๊ฐ๊ธฐ ๋๋ฌธ์ ์ ๊ฐ ์ฌ๊ธฐ๋ก ๋์ด๊ฐ ์ด์ ์ ๋๋ค.
const DecoratedComponent = require('../DecoratedComponent').default;
์ด๋ฌํ ์ข ๋ฅ์ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ฌ์ฉํ๋ฉด IDE์์ ์ํ์ ํ์ํ๊ณ ts ์ค๋ฅ๋ฅผ ์จ๊ธธ ์ ์์ต๋๋ค. ์ฝ๊ฐ ๋ชป์๊ฒผ์ง๋ง ๋ฐ์ฝ๋ ์ดํฐ๋ ๋จ์ํ ์ฐ๊ฒฐ๋ณด๋ค ๋ ์ข์ ๋ณด์ ๋๋ค. ํนํ ๋ฒ์ญ ๋๋ ํ์๊ณผ ๊ฐ์ ์ผ๋ถ ์ ํ๊ธฐ๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํ๋๋ก ๊ตฌ์ฑํ๋ ๊ฒฝ์ฐ
react-redux ๋ฌธ์ ๊ฐ ์๋๋๋ค. @withRouter (React-Router์์) ๋ฐ @graphql๊ณผ ๋์ผํ ์๊ฒฌ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
Typescript๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์ํ์ ์ฃผ์ ํ ์ ์๋ค๋ ๊ฒ์ ์ดํดํ์ง ๋ชปํ๋ ๊ฒ ๊ฐ์ต๋๋ค...
Typescript๋ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์ํ์ ์ฃผ์ ํ ์ ์๋ค๋ ๊ฒ์ ์ดํดํ์ง ๋ชปํ๋ ๊ฒ ๊ฐ์ต๋๋ค...
ํ ์ด๊ฒ ์์ง๋ ๋ฌธ์ ์ผ? ๋ฐฉ๊ธ ์ด๊ฒ์ ๋ถ๋ช์ณค์ต๋๋ค.
๋ฌด์ธ๊ฐ๊ฐ ์ฐ๋ฆฌ์ ๋๋๋ฅผ ํ๊ดดํ๊ณ ์๋ค๋ ์ฌ์ค์ด ์ฌํ์ง๋ง, ์ฐ๋ฆฌ๋ฅผ ๋ ํธ์ํ๊ฒ ํด์ฃผ๋๋ก ์ค๊ณ๋์์ต๋๋ค :(
๋ฌด์ธ๊ฐ๊ฐ ์ฐ๋ฆฌ์ ๋๋๋ฅผ ํ๊ดดํ๊ณ ์๋ค๋ ์ฌ์ค์ด ์ฌํ์ง๋ง, ์ฐ๋ฆฌ๋ฅผ ๋ ํธ์ํ๊ฒ ํด์ฃผ๋๋ก ์ค๊ณ๋์์ต๋๋ค :(
์ ๋ ์ :(
์ด๊ฒ์ ์๋ง๋ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๋ค๋ฃจ์ง๋ ์์ง๋ง ์ ์๊ฒ ์ ์๋ํ์ต๋๋ค.
๋ํ ํ ๊ณณ์์ ๋ด ์คํ ์ด ์ ํ(MyAppStore)์ ์ถ๊ฐํ ์ ์๋ ๊ธฐํ๋ ์ ๊ณตํฉ๋๋ค.
export function connectTs<TDispatchProps={}, TOwnProps=any>(
mapStateToProps?: (state: MyAppStore, ownProps?: TOwnProps) => any,
mapDispatchToProps?: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
): any {
return connect(mapStateToProps, mapDispatchToProps)
}
์ ๋ฐ์ดํธ๊ฐ ์์ต๋๊น?
connect๋ฅผ ๋ฐ์ฝ๋ ์ดํฐ๋ก๋ ์ฌ์ฉํ๊ณ ์ถ์ต๋๋ค.
๊ณต์ ์๋ฃจ์
์ด ์ ๊ณต๋๋์???
๋ง์ ๊ฐ๋ฐ์๊ฐ connect(mapStateToProps,mapDispatchToProps)(components)๋ณด๋ค @connect ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ ํธํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
@connect
์ง์ ์ฌ์ฉํ๋ ค๋ฉด(์ฌ์ฉ์ ์ง์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋์ ํ์ง ์๊ณ ) ๋ค์ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์ฌ์ฉํฉ๋๋ค.๊ทธ๋ฌ๋ ๋๋ ๋ชจ๋ ํ์ดํ์ด ์ค์ ๋ก ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ด ์๋๋ผ๋ @seansfkelley์ ๋ง์ ์ ์ ์ผ๋ก ๋์ํฉ๋๋ค...