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)

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.

Leave a Reply