-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
suggestion: connected generic component #55
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
Hi!
// src/containers/HelloContainer.tsx
import * as actions from '../actions/';
import { ComponentProps, DispatchFromProps, StateFromProps, StoreState } from '../types';
import { Dispatch } from 'react-redux';
import '../components/Hello.css';
import { connect } from 'react-redux';
import Hello from '../components/Hello';
function mapStateToProps({ enthusiasmLevel }: StoreState): StateFromProps {
return {
enthusiasmLevel,
};
}
function mapDispatchToProps(
dispatch: Dispatch<actions.EnthusiasmAction>
): DispatchFromProps {
return {
onIncrement: () => dispatch(actions.incrementEnthusiasm()),
onDecrement: () => dispatch(actions.decrementEnthusiasm()),
};
}
export default function HelloContainer<T>() {
return connect<StateFromProps, DispatchFromProps>(
mapStateToProps, mapDispatchToProps
)(Hello as new(props: ComponentProps<T>) => Hello<T>);
} use function HelloContainer // src/index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import { createStore } from 'redux';
import { enthusiasm } from './reducers';
import { StoreState } from './types';
import HelloContainer from './containers/HelloContainer';
import { Provider } from 'react-redux';
const store = createStore<StoreState>(enthusiasm, {
enthusiasmLevel: 1,
});
// Assign a type
const HelloNumber = HelloContainer<number>();
const HelloString = HelloContainer<string>();
ReactDOM.render(
<Provider store={store}>
<div>
<HelloNumber
name={555}
/>
<HelloString
name={'TypeScript'}
/>
</div>
</Provider>,
document.getElementById('root') as HTMLElement
);
registerServiceWorker(); // src/types/index.tsx
export interface StoreState {
enthusiasmLevel: number;
}
export interface StateFromProps {
enthusiasmLevel: number;
}
// merged type
export declare type ComponentProps<T> = StateFromProps & OwnProps<T> & DispatchFromProps;
// the type we want to make variable
export interface OwnProps<T> {
name: T;
}
export interface DispatchFromProps {
onIncrement: () => void;
onDecrement: () => void;
} // src/components/Hello.tsx
import * as React from 'react';
import './Hello.css';
import { ComponentProps } from '../types';
class Hello<T> extends React.Component<ComponentProps<T>> {
constructor(props: ComponentProps<T>) {
super(props);
}
render() {
const { name, enthusiasmLevel = 1, onIncrement, onDecrement } = this.props;
if (enthusiasmLevel <= 0) {
throw new Error('You could be a little more enthusiastic. :D');
}
return (
<div className="hello">
<div className="greeting">
Hello {name + getExclamationMarks(enthusiasmLevel)}
</div>
<div>
<button onClick={onDecrement}>-</button>
<button onClick={onIncrement}>+</button>
</div>
</div>
);
}
}
export default Hello;
// helpers
function getExclamationMarks(numChars: number) {
return Array(numChars + 1).join('!');
} |
The example above from @Zummer works like a charm. I'll try to create a pull request for this example. |
@Zummer Thanks for your great answer and also inspiring me to use less verbose interface names! |
What about this setup: import * as React from 'react'
import { connect } from 'react-redux'
const mapStateToProps = (storeSate: any) => {
return {
foo: 144
}
}
const container = connect(mapStateToProps)
interface TInjectedProps {
foo: number
}
export function hoc1<TRequiredProps extends TInjectedProps>(Component: React.ComponentType<TRequiredProps>) {
const connected = container(Component)
}
export function hoc2<TRequiredProps>(Component: React.ComponentType<TRequiredProps & TInjectedProps>) {
const connected = container(Component)
}
export function hoc3<TRequiredProps extends {}>(Component: React.ComponentType<TRequiredProps & TInjectedProps>) {
const connected = container(Component)
} In all three cases I get the error:
|
+1 import React from "react";
import { Subtract } from "utility-types";
import { connect } from "react-redux";
import { rangeVisibilitySelector } from "./date-range.selectors";
interface IInjectedProps {
visible: boolean;
}
interface IMappedProps {
isVisible: boolean;
}
const withIsVisibleRange = <T extends IInjectedProps>(
Component: React.ComponentType<T>
) => {
const WrappedComponent: React.SFC<
Subtract<T, IInjectedProps> & IMappedProps
> = ({ isVisible, ...rest }: IMappedProps) => {
return <Component {...rest} visible={isVisible} />;
};
const mapStateToProps = (state: ApplicationState) => ({
isVisible: rangeVisibilitySelector(state)
});
return connect(
mapStateToProps,
null
)(WrappedComponent);
};
export default withIsVisibleRange; In this case I get:
|
@IssueHunt has funded $50.00 to this issue.
|
@Zummer: is there an equivalent to your suggestion above, but for use with functional components rather than class components? |
Actually, scratch that, think I found it:
Seems to work. Anyone got any thoughts on whether this is/isn't a good approach? One thing I wondered was what is the best thing to return from the Thanks for the original workaround, @Zummer. Very helpful! |
Still can't get this working. I have this per comments, and no typescript errors, but the component doesn't render anything. My thoughts is it would need to be something like this |
This is how I am doing now a days... hope it helps. typescript class with redux
Hooks same way... just change the name actually React Hooks With Typescript
|
This still doesn't fix the issue of using generics on your class properties. Here is my class code.
But I still get this warning when I try to use the component.
TS2558: Expected 0 type arguments, but got 1. |
The repository already contains nice examples of generic components (generic-list) and connected components (sfc-counter-connected), but I'm having problems with the correct declaration and usage of connected generic components.
I would like to be able to write something like:
export const ConnectedListExtended<T> = connect<GenericListProps<T>, {}, OwnProps>(mapStateToProps)(GenericList<T>);
An example of the combination of these two examples would be really helpfull.
Thanks in advance!
The text was updated successfully, but these errors were encountered: