Javascript 如何在返回下一个js时更改滚动行为?

Javascript 如何在返回下一个js时更改滚动行为?,javascript,reactjs,next.js,Javascript,Reactjs,Next.js,我在索引js中获取一个帖子列表,如下所示: const Index = (props) => { return ( <div> {props.posts.map((each) => { return ( <Link scroll={false} as={`/post/${each.id}`} href="/post/[id]"

我在索引js中获取一个帖子列表,如下所示:

const Index = (props) => {
    return (
        <div>
            {props.posts.map((each) => {
                return (
                    <Link scroll={false} as={`/post/${each.id}`} href="/post/[id]" key={each.id}>
                        <a>
                            <h1>{each.title}</h1>
                        </a>
                    </Link>
                );
            })}
        </div>
    );
};

export async function getStaticProps() {
    const url = `https://jsonplaceholder.typicode.com/posts`;
    const res = await axios.get(url);

    return {
        props: { posts: res.data }
    };
}
import UserContext from '../context/context';

function MyApp({ Component, pageProps }) {
    const [ scrollPos, setScrollPos ] = React.useState(0);
    return (
        <UserContext.Provider value={{ scrollPos: scrollPos, setScrollPos: setScrollPos }}>
            <Component {...pageProps} />
        </UserContext.Provider>
    );
}

export default MyApp;
const Index=(道具)=>{
返回(
{props.posts.map((每个)=>{
返回(
{each.title}
);
})}
);
};
导出异步函数getStaticProps(){
常量url=`https://jsonplaceholder.typicode.com/posts`;
const res=等待axios.get(url);
返回{
道具:{posts:res.data}
};
}
当用户点击任何链接时,它会进入post页面,即:

function post({ post }) {
    return (
            <h1>{post.id}</h1>
    );
}

export async function getServerSideProps({ query }) {
    const { id } = query;
    const res = await Axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);

    return {
        props: { post: res.data}
    };
}
函数post({post}){
返回(
{post.id}
);
}
导出异步函数getServerSideProps({query}){
const{id}=query;
const res=等待Axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
返回{
道具:{post:res.data}
};
}
问题是当我点击后退时,滚动位置重置为顶部,它会获取所有帖子。 我在链接中包括了
scroll={false}
,但它不起作用


当用户从帖子页面单击后退时,如何防止滚动重置?

我通过上下文和窗口滚动位置的帮助解决了这个问题,如下所示:

const Index = (props) => {
    return (
        <div>
            {props.posts.map((each) => {
                return (
                    <Link scroll={false} as={`/post/${each.id}`} href="/post/[id]" key={each.id}>
                        <a>
                            <h1>{each.title}</h1>
                        </a>
                    </Link>
                );
            })}
        </div>
    );
};

export async function getStaticProps() {
    const url = `https://jsonplaceholder.typicode.com/posts`;
    const res = await axios.get(url);

    return {
        props: { posts: res.data }
    };
}
import UserContext from '../context/context';

function MyApp({ Component, pageProps }) {
    const [ scrollPos, setScrollPos ] = React.useState(0);
    return (
        <UserContext.Provider value={{ scrollPos: scrollPos, setScrollPos: setScrollPos }}>
            <Component {...pageProps} />
        </UserContext.Provider>
    );
}

export default MyApp;
从“../context/context”导入用户上下文;
函数MyApp({Component,pageProps}){
常量[scrollPos,setScrollPos]=React.useState(0);
返回(
);
}
导出默认MyApp;
索引js文件:

import Link from 'next/link';
import UserContext from '../context/context';
import { useContext } from 'react';
import Axios from 'axios';

export default function Index(props) {
    const { scrollPos, setScrollPos } = useContext(UserContext);

    const handleScrollPos = () => {
        setScrollPos(window.scrollY);
    };

    React.useEffect(() => {
        window.scrollTo(0, scrollPos);
    }, []);

    React.useEffect(() => {
        window.addEventListener('scroll', handleScrollPos);
        return () => {
            window.removeEventListener('scroll', handleScrollPos);
        };
    }, []);

    if (props.err) {
        return <h4>Error bro</h4>;
    }

    return (
        <div>
            {props.res.map((each) => {
                return (
                    <div key={each.id}>
                        <Link scroll={true} as={`/post/${each.id}`} href="/post/[id]">
                            {each.title}
                        </Link>
                    </div>
                );
            })}
        </div>
    );
}

export async function getServerSideProps() {
    let res;
    let err;
    try {
        res = await Axios.get('https://jsonplaceholder.typicode.com/posts');
        err = null;
    } catch (e) {
        err = 'Error bro';
        res = { data: [] };
    }

    return {
        props: {
            res: res.data,
            err: err
        }
    };
}
import Axios from 'axios';

function Post(props) {
    if (props.err) {
        return <h4>{props.err}</h4>;
    }
    return <h1>{props.post.title}</h1>;
}

export async function getServerSideProps(ctx) {
    const { query } = ctx;
    let err;
    let res;
    try {
        res = await Axios.get(`https://jsonplaceholder.typicode.com/posts/${query.id}`);
        err = null;
    } catch (e) {
        res = { data: [] };
        err = 'Error getting post';
    }

    return {
        props: {
            post: res.data,
            err: err
        }
    };
}

export default Post;
从“下一个/链接”导入链接;
从“../context/context”导入UserContext;
从“react”导入{useContext};
从“Axios”导入Axios;
导出默认功能索引(道具){
const{scrollPos,setScrollPos}=useContext(UserContext);
常量handleScrollPos=()=>{
设置滚动位置(窗口滚动);
};
React.useffect(()=>{
scrollTo窗口(0,scrollPos);
}, []);
React.useffect(()=>{
window.addEventListener('scroll',handleScrollPos);
return()=>{
window.removeEventListener('scroll',handleScrollPos);
};
}, []);
如果(道具错误){
返回错误bro;
}
返回(
{props.res.map((每个)=>{
返回(
{each.title}
);
})}
);
}
导出异步函数getServerSideProps(){
让我们重新来过;
让我们犯错;
试一试{
res=等待Axios.get('https://jsonplaceholder.typicode.com/posts');
err=null;
}捕获(e){
err='Error bro';
res={data:[]};
}
返回{
道具:{
res:res.data,
呃:呃
}
};
}
发布js文件:

import Link from 'next/link';
import UserContext from '../context/context';
import { useContext } from 'react';
import Axios from 'axios';

export default function Index(props) {
    const { scrollPos, setScrollPos } = useContext(UserContext);

    const handleScrollPos = () => {
        setScrollPos(window.scrollY);
    };

    React.useEffect(() => {
        window.scrollTo(0, scrollPos);
    }, []);

    React.useEffect(() => {
        window.addEventListener('scroll', handleScrollPos);
        return () => {
            window.removeEventListener('scroll', handleScrollPos);
        };
    }, []);

    if (props.err) {
        return <h4>Error bro</h4>;
    }

    return (
        <div>
            {props.res.map((each) => {
                return (
                    <div key={each.id}>
                        <Link scroll={true} as={`/post/${each.id}`} href="/post/[id]">
                            {each.title}
                        </Link>
                    </div>
                );
            })}
        </div>
    );
}

export async function getServerSideProps() {
    let res;
    let err;
    try {
        res = await Axios.get('https://jsonplaceholder.typicode.com/posts');
        err = null;
    } catch (e) {
        err = 'Error bro';
        res = { data: [] };
    }

    return {
        props: {
            res: res.data,
            err: err
        }
    };
}
import Axios from 'axios';

function Post(props) {
    if (props.err) {
        return <h4>{props.err}</h4>;
    }
    return <h1>{props.post.title}</h1>;
}

export async function getServerSideProps(ctx) {
    const { query } = ctx;
    let err;
    let res;
    try {
        res = await Axios.get(`https://jsonplaceholder.typicode.com/posts/${query.id}`);
        err = null;
    } catch (e) {
        res = { data: [] };
        err = 'Error getting post';
    }

    return {
        props: {
            post: res.data,
            err: err
        }
    };
}

export default Post;
从“Axios”导入Axios;
功能岗(道具){
如果(道具错误){
返回{props.err};
}
返回{props.post.title};
}
导出异步函数getServerSideProps(ctx){
const{query}=ctx;
让我们犯错;
让我们重新来过;
试一试{
res=等待Axios.get(`https://jsonplaceholder.typicode.com/posts/${query.id}`);
err=null;
}捕获(e){
res={data:[]};
err='获取post时出错';
}
返回{
道具:{
帖子:res.data,
呃:呃
}
};
}
导出默认帖子;
因此,当您从PostJS页面单击back时,index js中的第一个useEffect将运行,您将滚动到该位置


此外,第二个useEffect将通过侦听scroll事件侦听器捕获用户的滚动位置,因此它将始终在上下文中保存最新的滚动y位置,以便下次您返回索引js时,第一个useEffect将运行并在上下文中将滚动位置设置为该值。

scroll={false}
不保持上一页的滚动;它根本不会更改滚动条,这意味着滚动条将是您链接的页面的滚动条。您可以使用
scroll={false}
覆盖将scrollY设置为0的默认行为,以便实现您自己的行为

下面是我如何实现恢复滚动位置的。这与Max william的答案非常相似,但使用了
useRef
而不是
useState
。我们使用的是
useRef
而不是
useState
,因为
useRef
useState
不同,每当其值发生变化时,都不会导致重新渲染。每次用户更改滚动时,我们都会将该值更新到当前滚动位置,这意味着如果我们使用useState,将有大量无用的重新渲染

首先,定义UserContext组件,以便轻松地将滚动数据从_app.js组件传递到需要的任何位置:

import { createContext } from 'react';

const UserContext = createContext();

export default UserContext;
然后,在_app.js组件中,使用UserContext包装页面,并创建一个useRef属性来存储滚动位置

import { useRef } from 'react';
import UserContext from '../components/context'

function MyApp({ Component, pageProps }) {

  const scrollRef = useRef({
      scrollPos: 0
  });
    
  return (
      <Layout>
          <UserContext.Provider value={{ scrollRef: scrollRef }}>
              <Component {...pageProps} />
          </UserContext.Provider>
      </Layout>
  )
}

export default MyApp
import UserContext from '../components/context'
import { useContext } from 'react';

export default function YourPageComponent() {

  const { scrollRef } = useContext(UserContext);

  React.useEffect(() => {
    
    //called when the component has been mounted, sets the scroll to the currently stored scroll position
    window.scrollTo(0, scrollRef.current.scrollPos);

    const handleScrollPos = () => {
      //every time the window is scrolled, update the reference. This will not cause a re-render, meaning smooth uninterrupted scrolling.
      scrollRef.current.scrollPos = window.scrollY
    };

    window.addEventListener('scroll', handleScrollPos);

    return () => {
      //remove event listener on unmount
      window.removeEventListener('scroll', handleScrollPos);
    };
  });

  return (
    //your content
  )
}
最后一点是在链接组件上使用
scroll={false}
,链接回页面组件。因此,next.js不会自动将滚动设置为0,从而覆盖我们所做的一切


要归功于Max william对大部分结构的回答,我的主要更改是使用useRef。我还加了一些解释,希望对大家有帮助

我很惊讶这对您有效-我发现使用useState钩子会导致每次用户滚动时整个页面重新呈现(因为您在每个滚动事件上设置了状态),从而导致滚动时出现可见的延迟/震荡。在我看来,一个更好的解决方案是使用useRef钩子,它在渲染之间保持一个值,而不会在设置时导致重新渲染-这样,该值始终与当前滚动最新,但不会导致不必要的重新渲染。@Miles useffect钩子将导致