-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
suggestion: connected generic component #55
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: