Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to avoid resetting my useRef when setting state inside a useEffect?

I’ve built a draggable slider component inside one of my pages, which changes the state of the info on a form as the tab is dragged further along the screen (horizontal only) but as the position changes and I set state in order to change the info on this form, it causes my page to re-render and as I am using useRef, it causes the page to re-render and useRef to be reset back to its original value.

How can I set the state so that the info on this form changes without re-rendering? so no matter if the user drags the tab left or right on the slider, it will render the correct info on the form and not reset the useRef once state is set.

This is an image of the slider to give you an idea, (the white tab is the draggable part)

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

enter image description here

And here is my code:

const FinancialLandingPage = () => {
    const { t } = useTranslation();

    const [selectedPlanDetails, setSelectedPlanDetails] = useState(SUBSCRIPTION_PLANS.newbie.name);

    const DraggableComponent = () => {
        const [pressed, setPressed] = useState(false);
        const [position, setPosition] = useState({ x: 0, y: 0 });
        const [currentPlan, setCurrentPlan] = useState(1);
        const ref = useRef();

        // Monitor changes to position state and update DOM
        useEffect(() => {
            if (ref.current) {
                ref.current.style.transform = `translate(${position.x}px`;
            }

            if (position.x > 0 && position.x < 147) {
                setCurrentPlan(1);
                setSelectedPlanDetails(SUBSCRIPTION_PLANS.newbie.name);
            } else if (position.x > 147 && position.x < 294) {
                setCurrentPlan(2);
                setSelectedPlanDetails(SUBSCRIPTION_PLANS.enthusiast.name);
            } else if (position.x > 294 && position.x < 441) {
                setCurrentPlan(3);
                setSelectedPlanDetails(SUBSCRIPTION_PLANS.trader.name);
            } else if (position.x > 441 && position.x < 600) {
                setCurrentPlan(4);
                setSelectedPlanDetails(SUBSCRIPTION_PLANS.traderplus.name);
            }
        }, [position]);

        // Update the current position if mouse is down
        const onMouseMove = useCallback(
            (event) => {
                if (pressed) {
                    if (position.x + event.movementX < 0) {
                        setPosition({
                            x: 0,
                            y: position.y + 0,
                        });
                        return;
                    }

                    if (position.x + event.movementX >= 600) {
                        setPosition({
                            x: 610,
                            y: position.y + 0,
                        });
                        return;
                    }

                    setPosition({
                        x: position.x + event.movementX,
                        y: position.y + 0,
                    });
                }
            },
            [pressed, position]
        );
        
        return (
            <div
                ref={ref}
                onMouseMove={onMouseMove}
                onMouseDown={() => setPressed(true)}
                onMouseUp={() => setPressed(false)}
            >
                <div className="slider-bar-item">
                    <Heading level={"5"}>{t(base_t + "page_five.slider").replace("[num]", currentPlan)}</Heading>
                </div>
            </div>
        );
    };

return (
                    <>
                     <div className={"slider"}>
                                <div className="slider-bar">
                                    <DraggableComponent />
                                </div>
                            </div>

                            <div className={"form-container"}>
                                <div className={"header"}>
                                    <Heading align={"center"} level={"3"}>
                                        {t(base_t + "page_five.form.title")}
                                    </Heading>
                                    <Heading align={"center"} level={"1"}>
                                        {t(base_t + 
                              `page_five.form.${selectedPlanDetails}.price`)}
                                    </Heading>
                                    <Heading align={"center"} level={"6"}>
                                        {t(base_t + "page_five.form.price_description")}
                                    </Heading>
                                </div>
                                <div className={"form"}>
                                    <div className={"form-item"}>
                                        <CryptoIcon name={"checkmark"} />
                                        <Text size={16}>
                                            {t(base_t + 
                          `page_five.form.${selectedPlanDetails}.item_one`)}
                                        </Text>
                                    </div>
                                    <div className={"form-item"}>
                                        <CryptoIcon name={"checkmark"} />
                                        <Text size={16}>
                                            {t(base_t + 
                          `page_five.form.${selectedPlanDetails}.item_two`)}
                                        </Text>
                                    </div>
                                    <div className={"form-item"}>
                                        <CryptoIcon name={"checkmark"} />
                                        <Text size={16}>
                                            {__(base_t + 
                          `page_five.form.${selectedPlanDetails}.item_three`)}
                                        </Text>
                                    </div>
                                    <Button className={"button"}>
                                        {t(base_t + "page_five.form.button")}
                                        <Icon name={"chevron_right"} />
                                    </Button>
                                </div>
                            </div>
</>
);

export default FinancialLandingPage;

Thanks in advance!

>Solution :

I think your issue is similar to the one described here: https://stackoverflow.com/a/75048172/8926512

In short, you are redefining DraggableComponent on every rerender of the FinancialLandingPage component, causing DraggableComponent to be treated as a "new type" of component each time. As a result, during reconciliation, React will replace the old component type with the new component type and give it its initial state.

To fix this, you should move the definition of DraggableComponent outside the FinancialLandingPage component, and make any dependencies on FinancialLandingPage passed in as props to DraggableComponent.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading