简体   繁体   中英

Dynamic navigation React router dom

I have a list of restaurants. I want to click on the name of a restaurant to go to its page. I use "react-router-dom": "^6.4.4"

In the file RestaurantItem.jsx I did the following:

const navigate = useNavigate()
...
<div onClick={() => navigate('/restaurant/' + restaurant.id)} className={cl.name}>
    {restaurant.name}
</div>

And I wrote this routing:

<BrowserRouter>
    <Routes>
        <Route path="/" element={<Header/>}>
            <Route index element={<Main/>}/>
            <Route path="/restaurant/:id" element={<Restaurant/>}/>
            <Route
                path="*"
                element={<Navigate to="/" replace />}
            />
        </Route>
    </Routes>
</BrowserRouter>);

Now when you click on the name of the restaurant, the address in the address bar of the browser changes as it should. But I see a white screen in front of me. If I refresh the page, I see the restaurant page. There is no such problem when switching back to the main one.

Here is what I see in the console: 在此处输入图像描述

While the React logs are extremely uninformative to me, all I understand is that some homepage components get null in their props and therefore React can't render the homepage. But at the same time, I already want to go to another page and I don't need the main one now.

Here are the components that are mentioned in the logs.

usePressingButton:

export const usePressingButton = (buttonRef) => {
    const [isPressing, setIsPressing] = useState(false)

    useEffect(() => {
        buttonRef.current.addEventListener('mousedown', (event) => setIsPressing(true))
        buttonRef.current.addEventListener('mouseup', (event) => setIsPressing(false))
        return () => {
            buttonRef.current.removeEventListener('mousedown', (event) => setIsPressing(true))
            buttonRef.current.removeEventListener('mouseup', (event) => setIsPressing(false))
        }
    }, [])

    return isPressing
};

SelectCuisineLine:

const SelectCuisineLine = ({cuisines, setCuisines, selectСuisine}) => {
    const lineRef = useRef()
    const innerLineVisorRef = useRef()
    const rightArrow = useRef()
    const leftArrow = useRef()
    const leftRedSignal = useRef()
    const rightRedSignal = useRef()

    const [arrowsPressed, setArrowsPressed] = useState({left: false, right: false})
    const [linePosition, setLinePosition] = useState(0)
    const [limitPositions, setLimitPositions] = useState({left: true, right: false})
    const [lineLength, setLineLength] = useState(0)
    const [step, setStep] = useState(0)
    const [currentCuisine, setCurrentCuisine] = useState('ALL')

    useEffect(() => {
        setLineLength(cuisines.length * 130)
        setStep(Math.floor(getLineVisorWidth() / 130) * 130)
    }, [])

    useEffect(() => {
        rightArrow.current.addEventListener('mousedown', (event) => setArrowsPressed({
            ...arrowsPressed,
            right: true
        }));
        rightArrow.current.addEventListener('mouseup', (event) => setArrowsPressed({
            ...arrowsPressed,
            right: false
        }));
        leftArrow.current.addEventListener('mousedown', (event) => setArrowsPressed({
            ...arrowsPressed,
            left: true
        }));
        leftArrow.current.addEventListener('mouseup', (event) => setArrowsPressed({
            ...arrowsPressed,
            left: false
        }));
        return () => {
            rightArrow.current.removeEventListener('mousedown', (event) => setArrowsPressed({
                ...arrowsPressed,
                right: true
            }));
            rightArrow.current.removeEventListener('mouseup', (event) => setArrowsPressed({
                ...arrowsPressed,
                right: false
            }));
            leftArrow.current.removeEventListener('mousedown', (event) => setArrowsPressed({
                ...arrowsPressed,
                left: true
            }));
            leftArrow.current.removeEventListener('mouseup', (event) => setArrowsPressed({
                ...arrowsPressed,
                left: false
            }));
        }
    }, [])

    useEffect(() => {
        if (arrowsPressed.right && limitPositions.right) {
            lineRef.current.setAttribute('style', 'left:' + (linePosition - 10) + 'px')
            rightRedSignal.current.setAttribute('style', 'box-shadow: 0 0 8px 2px red;')
        } else if (!arrowsPressed.right) {
            rightRedSignal.current.setAttribute('style', 'box-shadow: none;')
        }

        if (arrowsPressed.left && limitPositions.left) {
            lineRef.current.setAttribute('style', 'left:' + (linePosition + 10) + 'px')
            leftRedSignal.current.setAttribute('style', 'box-shadow: 0 0 8px 2px red;')
        } else if (!arrowsPressed.left) {
            leftRedSignal.current.setAttribute('style', 'box-shadow: none;')
        }
    }, [arrowsPressed])

    function getLineVisorWidth() {
        return parseInt(window.getComputedStyle(innerLineVisorRef.current).width.replace('px', ''), 10)
    }

    function leftMotion() {
        setLimitPositions({...limitPositions, right: false})

        if (linePosition + step > 0) {
            lineRef.current.setAttribute('style', 'left: 0')
            setLinePosition(0)
            setLimitPositions({...limitPositions, left: true})
            return
        }

        lineRef.current.setAttribute('style', 'left:' + (linePosition + step) + 'px')
        setLinePosition(linePosition + step)

        if (limitPositions.left && arrowsPressed.left) {
            lineRef.current.setAttribute('style', 'left:' + (linePosition + 10) + 'px')
        }
    }

    function rightMotion() {
        setLimitPositions({...limitPositions, left: false})

        lineRef.current.setAttribute('style', 'left:' + (linePosition - step) + 'px')
        setLinePosition(linePosition - step)

        const lineVisorWidth = getLineVisorWidth()
        if (lineVisorWidth > (lineLength - (Math.abs(linePosition))) - lineVisorWidth) {
            const limitRightPosition = lineLength - lineVisorWidth
            lineRef.current.setAttribute('style', 'left:' + (-limitRightPosition) + 'px')
            setLinePosition(-limitRightPosition)
            setLimitPositions({...limitPositions, right: true})
        }
    }



    return (
        <div className="externalLineVisor">
            <div style={{margin: "3px", float: "left"}}>
                <Arrow
                    direction='left'
                    onClick={leftMotion}
                    ref={leftArrow}
                />
            </div>
            <div className="innerLineVisor" ref={innerLineVisorRef}>
                <div className="cuisineLine" ref={lineRef}>
                    {cuisines.map(cuisine =>
                        <SelectCuisineButton
                            key={cuisine.id}
                            selectСuisine={selectСuisine}
                            currentCuisine={currentCuisine}
                            setCurrentCuisine={setCurrentCuisine}
                        >
                            {cuisine}
                        </SelectCuisineButton>
                    )}
                </div>
                <div className={limitPositions.left ? "leftBlur off" : "leftBlur"}/>
                <div className={limitPositions.right ? "rightBlur off" : "rightBlur"}/>
                <div className="leftRedSignal" ref={leftRedSignal}/>
                <div className="rightRedSignal" ref={rightRedSignal}/>
            </div>
            <div style={{margin: "3px", float: "right"}}>
                <Arrow
                    direction='right'
                    onClick={rightMotion}
                    ref={rightArrow}
                />
            </div>
        </div>
    );
};

export default SelectCuisineLine;

SpetialOffer:

const SpecialOffer = ({trySpecialOffer}) => {
    const leftArrowRef = useRef()
    const rightArrowRef = useRef()
    const isLeftArrowPressing = usePressingButton(leftArrowRef)
    const isRightArrowPressing = usePressingButton(rightArrowRef)

    const areaOfVisibilityRef = useRef()
    const [specialOffers, setSpecialOffers] = useState([])
    const [currentOfferIndex, setCurrentOfferIndex] = useState(0)
    const [areaOfVisibilityWidth, setAreaOfVisiblyWidth] = useState(0)
    const [lineLength, setLineLength] = useState(0)
    const [linePosition, setLinePosition] = useState(0)

    const [fetchSpecialOffers, isLoading, error] = useFetching(async () => {
        const response = await SpecialOfferAPI.getAll()
        setSpecialOffers(response.data)
    })

    useEffect(() => {
        fetchSpecialOffers()
        setAreaOfVisiblyWidth(areaOfVisibilityRef.current.offsetWidth)
    }, [])

    useEffect(() => {
        setLineLength(areaOfVisibilityWidth * specialOffers.length)
    }, [specialOffers])

    useEffect(() => {
        if (isLeftArrowPressing && linePosition === 0) {
            setLinePosition(20)
        }
        if (!isLeftArrowPressing && linePosition === 20) {
            setLinePosition(0)
        }
        if (isRightArrowPressing && linePosition === areaOfVisibilityWidth - lineLength) {
            setLinePosition(areaOfVisibilityWidth - lineLength - 20)
        }
        if (!isRightArrowPressing && linePosition === (areaOfVisibilityWidth - lineLength - 20)) {
            setLinePosition(areaOfVisibilityWidth - lineLength)
        }
    }, [isLeftArrowPressing, isRightArrowPressing])

    function rightMotion() {
        if (linePosition > areaOfVisibilityWidth - lineLength) {
            setLinePosition(linePosition - areaOfVisibilityWidth)
            setCurrentOfferIndex(currentOfferIndex + 1)
        }
    }

    function leftMotion() {
        if (linePosition < 0) {
            setLinePosition(linePosition + areaOfVisibilityWidth)
            setCurrentOfferIndex(currentOfferIndex - 1)
        }
    }

    function tryCurrentOffer() {
        trySpecialOffer(specialOffers[currentOfferIndex].restaurant)
    }

    return (
        <div className="specialOffer">
            <div className="somethingNewText">ЧТО-ТО НОВОЕ</div>
            <div className="specialOfferCenter">
                <div className="arrowArea">
                    <Arrow
                        direction='left'
                        onClick={leftMotion}
                        ref={leftArrowRef}
                    />
                </div>
                <div className="areaOfVisibility" ref={areaOfVisibilityRef}>
                    {areaOfVisibilityWidth > 0 &&
                        <SpecialOfferLine
                            specialOffers={specialOffers}
                            itemWidth={areaOfVisibilityWidth - 1}
                            lineLength={lineLength}
                            linePosition={linePosition}
                        />}
                </div>
                <div className="arrowArea right">
                    <Arrow
                        direction='right'
                        onClick={rightMotion}
                        ref={rightArrowRef}
                    />
                </div>
            </div>
            <div className="wantToTry" onClick={tryCurrentOffer}>ГДЕ ПОПРОБОВАТЬ?</div>
        </div>
    );
};

export default SpecialOffer;

Arrow:

const Arrow = forwardRef((props, ref) => (
    <div className="arrowContainer" onClick={props.onClick} ref={ref}>
        <div className={props.direction === 'left' ? "left1" : "left1 right1"}/>
        <div className={props.direction === 'left' ? "left2" : "left2 right2"}/>
    </div>
));

export default Arrow;

What am I doing wrong?

Short of a running code demo the closest thing I see to an issue is referencing the ref values when the component is unmounting in the useEffect hooks' cleanup functions. The event listeners should also be passed a stable handler reference. In other words, the function that is removed needs to be the same function that was added.

Try creating a listener callback for the events and saving a copy of the ref in the hook's callback scope and reference this in the cleanup functions.

Example:

export const usePressingButton = (buttonRef) => {
  const [isPressing, setIsPressing] = useState(false);

  useEffect(() => {
    const ref = buttonRef.current;

    const setTrue = (event) => setIsPressing(true);
    const setFalse = (event) => setIsPressing(false);

    ref.addEventListener('mousedown', setTrue);
    ref.addEventListener('mouseup', setFalse);

    return () => {
      ref.removeEventListener('mousedown', setTrue);
      ref.removeEventListener('mouseup', setFalse);
    }
  }, []);

  return isPressing;
};

Do the same thing for the other useEffect hook and handlers in the SelectCuisineLine component.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM