Reduxコンテナ を適切に入力する方法を理解するのに問題があります。
次のような単純なプレゼンテーションコンポーネントについて考えてみます。
interface MyProps {
name: string;
selected: boolean;
onSelect: (name: string) => void;
}
class MyComponent extends React.Component<MyProps, {}> { }
このコンポーネントの観点から、すべての小道具が必要です。
次に、これらすべての小道具を状態から外すコンテナーを作成します。
function mapStateToProps(state: MyState) {
return {
name: state.my.name,
selected: state.my.selected
};
}
function mapDispatchToProps(dispatch: IDispatch) {
return {
onSelect(name: string) {
dispatch(mySelectAction(name));
}
};
}
const MyContainer = connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
これは機能しますが、大きなタイピングの問題があります。マッピング関数(mapStateToProps
およびmapDispatchToProps
)は、MyProps
を満たすための適切なデータを提供しているという保護がありません。これは、エラー、タイプミス、および不十分なリファクタリングの傾向があります。
マッピング関数にタイプMyProps
を返させることができます:
function mapStateToProps(state: MyState): MyProps { }
function mapDispatchToProps(dispatch: IDispatch): MyProps { }
ただし、すべてのMyProp
プロパティをオプションにしない限り、これは機能しません。そのため、各マッピング関数は、必要な部分のみを返すことができます。小道具はプレゼンテーションコンポーネントではオプションではないため、オプションにしたくありません。
別のオプションは、各マップ関数の小道具を分割し、コンポーネント小道具のためにそれらを結合することです:
// MyComponent
interface MyStateProps {
name: string;
selected: boolean;
}
interface MyDispatchProps {
onSelect: (name: string) => void;
}
type MyProps = MyStateProps & MyDispatchProps;
class MyComponent extends React.Component<MyProps, {}> { }
// MyContainer
function mapStateToProps(state: MyState): MyStateProps { }
function mapDispatchToProps(dispatch: IDispatch): MyDispatchProps { }
わかりました。これで私は自分の望むものに近づいていますが、それはうるさくて、コンテナの形の周りにプレゼンテーションコンポーネントのプロップインターフェイスを書くので、私は好きではありません。
そして今、2番目の問題が発生します。コンテナーを別のコンポーネント内に配置する場合はどうなりますか?
<MyContainer />
これにより、name
、selected
、およびonSelect
がすべて見つからないというコンパイルエラーが発生します...しかし、コンテナーがReduxに接続してそれらを提供しているため、これは意図的なものです。したがって、これによりすべてのコンポーネントの小道具をオプションにすることに戻りますが、実際にはオプションではないので、私はそれが好きではありません。
MyContainer
に渡したい独自の小道具がいくつかあると、事態はさらに悪化します。
<MyContainer section="somethng" />
今私がやろうとしていることは、section
の必須の小道具MyContainer
を持っていますが、MyComponent
の小道具はなく、name
、selected
、およびonSelect
はMyComponent
の必須の小道具ですが、MyContainer
のすべての小道具はオプションであるか、またはありません。私はこれをどう表現するか全く途方に暮れています。
これに関するどんな指導もいただければ幸いです!
あなたは最後の例で正しい軌道に乗っています。また、定義する必要があるのはMyOwnProps
インターフェースであり、connect
関数を入力します。
これらのタイピング: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/react-redux/react-redux.d.ts 、次のようなことができます
interface MyProps {
section: string;
name: string;
selected: boolean;
onSelect: (name: string) => void;
}
interface MyStateProps {
name: string;
selected: boolean;
}
interface MyDispatchProps {
onSelect: (name: string) => void;
}
interface MyOwnProps {
section: string;
}
class MyComponent extends React.Component<MyProps, {}> { }
function mapStateToProps(state: MyState): MyStateProps { }
function mapDispatchToProps(dispatch: IDispatch): MyDispatchProps { }
const MyContainer = connect<MyStateProps, MyDispatchProps, MyOwnProps>(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
これにより、ownProps
とmapStateToProps
にmapDispatchToProps
と入力することもできます。
function mapStateToProps(state: MyState, ownProps: MyOwnProps): MyStateProps
ディスパッチタイプ、ステートタイプ、およびownPropsタイプを定義する限り、MyPropsタイプの交差タイプを定義する必要はありません。そうすることで、MyPropsを独自の場所に保持し、コンテナー、またはコンテナーなしでコンポーネントが使用される場所に、必要に応じて小道具を適用させることができます。これはあなたとあなたのユースケース次第だと思います-MyProps = MyStateProps & MyDispatchProps & MyOwnProps
、1つの特定のコンテナーに関連付けられているため、柔軟性が低くなります(ただし、詳細度は低くなります)。
解決策としてはかなり冗長ですが、必要な小道具のさまざまな部分がさまざまな場所で組み立てられ、connect
がそれらを結合することをTypeScriptに伝える方法はないと思います。
また、それだけの価値があるので、簡単にするために、通常はオプションの小道具を使いました。そのため、このアプローチを使用する上で共有する経験があまりありません。
Partial<MyProps>
、ここでPartial
は、次のように定義される組み込みのTypeScriptタイプです。
type Partial<T> = {
[P in keyof T]?: T[P];
}
インターフェースを取り、その中のすべてのプロパティをオプションにします。
これは、私が作成したプレゼンテーションとRedux対応のコンポーネントのペアの例です。
/ components/ConnectionPane/ConnectionPane.tsx
export interface IConnectionPaneProps {
connecting: boolean;
connected: boolean;
onConnect: (hostname: string, port: number) => void;
}
interface IState {
hostname: string;
port?: number;
}
export default class ConnectionPane extends React.Component<IConnectionPaneProps, IState> {
...
}
/ containers/ConnectionPane/ConnectionPane.ts
import {connect} from 'react-redux';
import {connectionSelectors as selectors} from '../../../state/ducks';
import {connect as connectToTestEcho} from '../../../state/ducks/connection';
import {ConnectionPane, IConnectionPaneProps} from '../../components';
function mapStateToProps (state): Partial<IConnectionPaneProps> {
return {
connecting: selectors.isConnecting(state),
connected: selectors.isConnected(state)
};
}
function mapDispatchToProps (dispatch): Partial<IConnectionPaneProps> {
return {
onConnect: (hostname, port) => dispatch(connectToTestEcho(hostname, port))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ConnectionPane) as any;
プレゼンテーションコンポーネントの小道具はオプションではありません。対応する「スマート」コンポーネントに関係なく、プレゼンテーションに必要なものとまったく同じです。
一方、mapStateToProps
とmapDispatchToProps
を使用すると、各関数で必要なプレゼンテーション小道具のサブセットを割り当てることができ、プレゼンテーション小道具インターフェースで定義されていない小道具にはエラーのフラグが付けられます。
interface MyStateProps {
name: string;
selected: boolean;
}
interface MyDispatchProps {
onSelect: (name: string) => void;
}
interface MyOwnProps {
section: string;
}
// Intersection Types
type MyProps = MyStateProps & MyDispatchProps & MyOwnProps;
class MyComponent extends React.Component<MyProps, {}> { }
function mapStateToProps(state: MyState): MyStateProps { }
function mapDispatchToProps(dispatch: IDispatch): MyDispatchProps { }
const MyContainer = connect<MyStateProps, MyDispatchProps, MyOwnProps>(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
Intersection Typesと呼ばれる使用法を使用できます https://www.typescriptlang.org/docs/handbook/advanced-types.html#交差タイプ