0

I'm currently playing and learning React's hooks. I wanted to re-use the capability to set an element's shadow (using bootstrap as css-framework).

This is what my App currently looks like:

export const App: React.FunctionComponent<IAppProps> = ({ }: IAppProps) => {
    // get shadow-classes for alert
    const [shadowClasses, setShadowClasses] = useShadow("none");

    // define callbacks when hovering the alert
    const handleMouseEnter = () => setShadowClasses("regular");
    const handleMouseOut = () => setShadowClasses("none");

    // return the markup to be used
    return (
        <Container>
            <Grid.Row>
                <Grid.Col>
                    <Alert color={Constants.Color.Danger} className={classNames(shadowClasses)} onMouseEnter={handleMouseEnter} onMouseOut={handleMouseOut} >{"This is some kind of an alert ..."}</Alert>
                </Grid.Col>
            </Grid.Row>
        </Container>
    );
}; 

My goal is to have an shadow added when the Alert is hovered. Unfortunately nothing happens when hovering and I don't get the why.

Find below the implementation of my "custom hook":

export function useShadow(initialType: "none"|"sm"|"regular"|"large"): [string[], (type: "none"|"sm"|"regular"|"large") => void] {
    // define the classes to be used
    const classNames: string[] = [];

    // get the shadow's current value
    const [shadowType, setShadow] = React.useState(initialType);

    // set depending on given type
    switch (shadowType) {
        case "none":
            classNames.push(`shadow-none`);
            break;
        case Constants.BreakpointSize.Small:
            classNames.push(`shadow-sm`);
            break;
        case "regular":
            classNames.push(`shadow`);
            break;
        case Constants.BreakpointSize.Large:
            classNames.push(`shadow-lg`);
            break;
    }

    // define the callback to change the shadow
    const handleChange = (type: Type) => () => setShadow(type);

    // return the class-names and the change-callback
    return [classNames, handleChange];
}

I'm not even sure if this is correct way on how to use custom hooks or not.

** Update **

I created a useSpacing hook to set spacing of elements which is implemented like:

export function useSpacing(initialSpacingProps: ISpacingProps[] = []): [string[], (spacingProps: ISpacingProps[]) => void] {
    // get the state-value
    const [spacingProps, setSpacingProps] = React.useState(initialSpacingProps);

    // create the result holding the class-names
    const spacingClasses: string[] = [];

    // loop through given spacing-definitions
    for (let spacingProp of spacingProps) {
        // get the values
        const { breakpoint, property, side, size, negative } = spacingProp;

        // handle depending on breakpoint
        spacingClasses.push(`${property}${side}${breakpoint !== Constants.BreakpointSize.ExtraSmall ? `-${breakpoint}` : ``}-${negative && size !== Size.Auto ? `n` : ``}${size}`);
    }

    // define the callback when the value should be changed
    const handleChange = (newSpacingProps: ISpacingProps[]) => setSpacingProps(newSpacingProps);

    // return the classes
    return [spacingClasses, handleChange];
}

and is used this way:

export const App: React.FunctionComponent<IAppProps> = ({ }: IAppProps) => {
    const initialSpacingProps = [
        {
            breakpoint: Constants.BreakpointSize.ExtraSmall,
            property: Spacing.Property.Margin,
            side: Spacing.Side.LeftRight,
            size: Spacing.Size.Two
        }
    ];

    const clickedSpacingProps = [
        {
            breakpoint: Constants.BreakpointSize.Small,
            property: Spacing.Property.Padding,
            side: Spacing.Side.TopBottom,
            size: Spacing.Size.Five
        }
    ];

    // get the classes to apply spacing accordingly
    const [spacingClasses, setSpacingClasses] = useSpacing(initialSpacingProps);

    // define the callback when jumbotron gets clicked
    const handleClick = React.useCallback(() => setSpacingClasses(clickedSpacingProps), []);

    // return the markup to be used
    return (
        <Container>
            <Grid.Row>
                <Grid.Col>
                    <Shadows.Shadow type={Constants.BreakpointSize.Large}>
                        <Jumbotron className={classNames(spacingClasses)} onClick={handleClick}>
                            <h1 className="display-4">Hello, world!</h1>
                        </Jumbotron>
                    </Shadows.Shadow>
                </Grid.Col>
            </Grid.Row>
        </Container>
    );
};

When clicking the jumbotron-element, the new spacing is applied correctly

9
  • Are handleMouseEnter and handleMouseOut being executed? Commented Jun 27, 2019 at 15:11
  • @FernandoMontoya yes, they are. I've added a console.log and both are showing for enter and out. I've also added a log in the hook's handleChange which never got hit. Commented Jun 27, 2019 at 15:14
  • Have you tried changing handleChange to use useCallback Hook? Commented Jun 27, 2019 at 15:18
  • @FernandoMontoya tried and still got not hit Commented Jun 27, 2019 at 15:19
  • 1
    @FernandoMontoya you are a life saver! Please post as answer so I can get you the credit you deserve ;) Commented Jun 27, 2019 at 15:35

1 Answer 1

1

Your handleChange function is returning another function, removing the second function will fix it.

const handleChange = (type: Type) => setShadow(type);

Demo: https://codesandbox.io/s/youthful-gould-offv6

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.