diff --git "a/playground\\src\\connected\\connected-counter-with-decorator.tsx" "b/playground\\src\\connected\\connected-counter-with-decorator.tsx" new file mode 100644 index 0000000..3e59c4b --- /dev/null +++ "b/playground\\src\\connected\\connected-counter-with-decorator.tsx" @@ -0,0 +1,66 @@ +// NOTE: Requires `"experimentalDecorators": true` in tsconfig.json +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; +import { RootState } from '../store/types'; +import { countersActions } from '../features/counters'; + +// Props passed by the parent component +interface OwnProps { + label: string; +} + +// Props mapped from Redux state via mapStateToProps +interface StateProps { + count: number; +} + +// Props mapped from Redux dispatch via mapDispatchToProps +interface DispatchProps { + onIncrement: () => void; + onDecrement: () => void; +} + +// All props combined — used as the component Props type +type Props = OwnProps & StateProps & DispatchProps; + +const mapStateToProps = ( + state: RootState, + _ownProps: OwnProps +): StateProps => ({ + count: state.counters.reduxCounter, +}); + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => + bindActionCreators( + { + onIncrement: countersActions.increment, + onDecrement: countersActions.decrement, + }, + dispatch + ); + +// Using @connect decorator +// Equivalent to: +// export default connect(mapStateToProps, mapDispatchToProps)(ConnectedCounterWithDecorator) +@connect( + mapStateToProps, + mapDispatchToProps +) +class ConnectedCounterWithDecorator extends Component { + render() { + const { label, count, onIncrement, onDecrement } = this.props; + + return ( +
+ + {label}: {count} + + + +
+ ); + } +} + +export default ConnectedCounterWithDecorator; diff --git "a/playground\\src\\connected\\connected-counter.tsx" "b/playground\\src\\connected\\connected-counter.tsx" new file mode 100644 index 0000000..4de8c8e --- /dev/null +++ "b/playground\\src\\connected\\connected-counter.tsx" @@ -0,0 +1,59 @@ +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; +import { RootState } from '../store/types'; +import { countersActions } from '../features/counters'; +import { hotelsActions } from '../features/hotels'; + +// Props that come from the parent component +interface OwnProps { + label: string; +} + +// Props from Redux state (mapStateToProps) +interface StateProps { + count: number; +} + +// Props from Redux dispatch (mapDispatchToProps) +interface DispatchProps { + onIncrement: () => void; + onDecrement: () => void; +} + +// All component props combined +type Props = OwnProps & StateProps & DispatchProps; + +// Map Redux state to component props +const mapStateToProps = (state: RootState, ownProps: OwnProps): StateProps => ({ + count: state.counters.reduxCounter, +}); + +// Map Redux dispatch to component props +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => + bindActionCreators( + { + onIncrement: countersActions.increment, + onDecrement: countersActions.decrement, + }, + dispatch + ); + +// Using the @connect decorator syntax +@connect(mapStateToProps, mapDispatchToProps) +class ConnectedCounter extends Component { + render() { + const { label, count, onIncrement, onDecrement } = this.props; + return ( +
+ + {label}: {count} + + + +
+ ); + } +} + +export default ConnectedCounter; diff --git "a/playground\\src\\features\\counters\\index.ts" "b/playground\\src\\features\\counters\\index.ts" new file mode 100644 index 0000000..c888571 --- /dev/null +++ "b/playground\\src\\features\\counters\\index.ts" @@ -0,0 +1,3 @@ +export { countersReducer } from './reducer'; +export { countersActions } from './actions'; +export { CounterState } from './types'; diff --git "a/playground\\tsconfig.json" "b/playground\\tsconfig.json" new file mode 100644 index 0000000..797e833 --- /dev/null +++ "b/playground\\tsconfig.json" @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "experimentalDecorators": true, + "baseUrl": "." + }, + "include": [ + "src" + ] +}