-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Enhancement] Add a nested HOCs example with connect #5
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
Comments
Hello, PS: If I ever use any part of your work I will give due credits. |
Also recently I wanted to rewrite and update entire HOC section, so this could give me some fresh ideas Related: #100 |
@BoostIO funded this issue with $20. Visit this issue on Issuehunt |
@IssueHunt has funded $30.00 to this issue.
|
I have added a new section with an example of nested HOC with import { RootState } from 'MyTypes';
import React from 'react';
import { connect } from 'react-redux';
import { Diff } from 'utility-types';
import { countersActions, countersSelectors } from '../features/counters';
// These props will be injected into the base component
interface InjectedProps {
count: number;
onIncrement: () => void;
}
export const withConnectedCount = <BaseProps extends InjectedProps>(
BaseComponent: React.ComponentType<BaseProps>
) => {
type HocProps = Diff<BaseProps, InjectedProps> & {
// here you can extend hoc with new props
initialCount?: number;
};
const mapStateToProps = (state: RootState) => ({
count: countersSelectors.getReduxCounter(state.counters),
});
const dispatchProps = {
onIncrement: countersActions.increment,
};
class Hoc extends React.Component<InjectedProps> {
// Enhance component name for debugging and React-Dev-Tools
static displayName = `withConnectedCount(${BaseComponent.name})`;
// reference to original wrapped component
static readonly WrappedComponent = BaseComponent;
render() {
const { count, onIncrement, ...restProps } = this.props;
return (
<BaseComponent
count={count} // injected
onIncrement={onIncrement} // injected
{...(restProps as BaseProps)}
/>
);
}
}
const ConnectedHoc = connect<
ReturnType<typeof mapStateToProps>,
typeof dispatchProps,
HocProps,
RootState
>(
mapStateToProps,
dispatchProps
)(Hoc);
return ConnectedHoc;
}; |
… recent TypeScript 3.7 and React & Redux type definitions. (#193) * Updated deps * Updated deps and refactored code to fix breaking changes * Fixed issue with HOC Fixed #111 * Added an example of nested HOC with connect. Fixed #5 * Updated readme Intro & TOC * Updated PR template * Added new section with Nested HOC - wrapping a component, injecting props and connecting to redux #5 * Updated dev deps
@piotrwitek has rewarded $35.00 to @piotrwitek. See it on IssueHunt
|
@piotrwitek thanks. Unfortunately I still have a type problem:
Could you please help me with that? In my case I don't use My code: import * as React from 'react';
import { translate, splitAndSubstitute } from 'fm3/stringUtils';
import { Diff } from 'utility-types';
import { connect } from 'react-redux';
import { RootState } from './storeCreator';
function tx(key: string, params: { [key: string]: any } = {}, dflt = '') {
const t = translate(window.translations, key, dflt);
return typeof t === 'function' ? t(params) : splitAndSubstitute(t, params);
}
export type Translator = typeof tx;
interface InjectedProps {
t: Translator;
}
export const withTranslator = <BaseProps extends InjectedProps>(
BaseComponent: React.ComponentType<BaseProps>,
) => {
type HocProps = Diff<BaseProps, InjectedProps>;
class Hoc extends React.Component<HocProps> {
static displayName = `injectL10n(${BaseComponent.name})`;
static readonly WrappedComponent = BaseComponent;
render() {
const { ...restProps } = this.props;
return <BaseComponent t={tx} {...(restProps as BaseProps)} />;
}
}
const mapStateToProps = (state: RootState) => ({
languageCounter: state.l10n.counter, // force applying english language on load
});
return connect<
ReturnType<typeof mapStateToProps>,
undefined,
HocProps,
RootState
>(mapStateToProps)(Hoc);
}; |
|
Thank you. Now HOC with type Props = {
t: Translator;
}
const FooInt: React.FC<Props> = ({ t }) => {
// ...
}
const Foo = withTranslator(FooInt);
...
<Foo />
// ^-- Property 't' is missing in type '{}' but required in type 'Pick<InjectedProps, "t">'. |
@zdila It should work just fine. Did you change anything else except the single line I have pointed out? Could you please copy your entire file again? |
Piotr, you can see it at https://github.com/FreemapSlovakia/freemap-v3-react/blob/46ae01320e276ed9eb55bc56af4f85d9a813a8bb/src/l10nInjector.tsx If you like, you can also clone the project (branch |
@zdila Thanks for branch, I have figured out your problem. The problem was that Hoc component was receiving incorrect type. It should be the return type of mapStateToProps, NOT an InjectedProps definition. Thanks for providing your use case, I have improved my example to better convey this distinction for other people. Here is a corrected code: import * as React from 'react';
import { translate, splitAndSubstitute } from 'fm3/stringUtils';
import { Diff } from 'utility-types';
import { connect } from 'react-redux';
import { RootState } from './storeCreator';
function tx(key: string, params: { [key: string]: any } = {}, dflt = '') {
const t = translate(window.translations, key, dflt);
return typeof t === 'function' ? t(params) : splitAndSubstitute(t, params);
}
export type Translator = typeof tx;
interface InjectedProps {
t: Translator;
}
export const withTranslator = <BaseProps extends InjectedProps>(
BaseComponent: React.ComponentType<BaseProps>,
) => {
const mapStateToProps = (state: RootState) => ({
languageCounter: state.l10n.counter, // force applying english language on load
});
type HocProps = ReturnType<typeof mapStateToProps>;
class Hoc extends React.Component<HocProps> {
static displayName = `injectL10n(${BaseComponent.name})`;
static readonly WrappedComponent = BaseComponent;
render() {
const { languageCounter, ...restProps } = this.props;
// TODO: do something with languageCounter
return <BaseComponent t={tx} {...(restProps as BaseProps)} />;
}
}
type OwnProps = Diff<BaseProps, InjectedProps>;
return connect<HocProps, undefined, OwnProps, RootState>(mapStateToProps)(
Hoc,
);
};
type Props = {
t: Translator;
};
declare const FooInt: React.FC<Props>;
const Foo = withTranslator(FooInt);
<Foo />; |
Thanks! Now it works for me 👍. |
You might consider writing up techniques for dealing with prop types on components with multiple nested HOCs. For example, I frequently have components with three layers of HOCs: React-Router, Redux, and Apollo-React (graphql client).
I have a handle on how to deal, but recent changes in both Typescript and various declarations have made these stricter and harder to get right. I'm not at all confident that my design pattern for this is the best, but I'll share if you've nothing.
IssueHunt Summary
Backers (Total: $50.00)
Submitted pull Requests
Tips
IssueHunt has been backed by the following sponsors. Become a sponsor
The text was updated successfully, but these errors were encountered: