reactjs - 如果用户已经在重定向页面上,history.push 会进行额外的重定向,并忽略搜索参数

标签 reactjs react-router react-router-dom

我有一个简单的 Navbar,里面有一个 Form:

const NavBar = () => {
   let history = useHistory()
   ...
   ...

   return (
     ...
     <Form inline onSubmit={handleSubmit}>
         <InputGroup style={{width: "90%"}}>
             <Form.Control id="navbar-search" placeholder="Pesquise" size="sm"/>
             <Form.Control as="select" size="sm">
                 <option>Ações</option>
                 <option>Declarações</option>
             </Form.Control>
             <InputGroup.Append  size="sm">
                 <Button size="sm" type="submit" variant="outline-secondary">
                     <SearchIcon fontSize="small" />
                 </Button>
             </InputGroup.Append>
         </InputGroup>
     </Form>
     ...
   )

handleSubit 假设重定向(使用 history.push)到我将使用输入值作为搜索参数的路径。我正在使用 react-router-dom

const handleSubmit = (e) => {
    let baseEndpoint = e.target[1].value === "Ações" ? "actions" : "quotes"
    history.push({
        pathname: `/${baseEndpoint}/query`,
        search: `?description=${e.target[0].value}`, 
    })
}

一切看起来都很好,除非用户在 handleSubmit 将要重定向的页面中,即在 /${baseEndpoint}/query

如果用户在此页面中,历史将转到 /${baseEndpoint}/query?description=${e.target[0].value} 并自动重新呈现到 /${baseEndpoint}/query?.

我也尝试过使用 history.replace,但没有用。

const handleSubmit = (e) => {
    let baseEndpoint = e.target[1].value === "Ações" ? "actions" : "quotes"
    let url = `/${baseEndpoint}/query`
    if(history.location.pathname === url) {
        history.replace({
            pathname: url,
            search: `?description=${e.target[0].value}`, 
        })
        return
    }
    history.push({
        pathname: url,
        search: `?description=${e.target[0].value}`, 
    })
}

是什么导致了这种行为?我做错了什么?

非常感谢!

(编辑)我的 SwitchRoute:

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";


class Wrapper extends Component {
  render() {
    return (
      <>
        <Container className="main">
          <MetaData />
          <Navbar />
          <this.props.component {...this.props} />
          <br />
          <br />
          <Footer />
        </Container>
      </>
    )
  }
}


export default function App() {
  return (
    <Router>
      <Switch>
        <Route exact path="/"
          render={props => (
            <Wrapper {...props} component={Home} />
          )}
        />
        <Route
          exact path="/actions"
          key="action-home"
          render={props => (
            <Wrapper
              {...props}
              image={bozoAction}
              baseEndpoint="actions"
              component={EntityHome}
            />
          )}
        />
        <Route
          path="/actions/query"
          key="action-query"
          render={props => (
            <Wrapper
              {...props}
              image={bozoAction}
              baseEndpoint="actions"
              component={EntityQuery}
            />
          )}
        />
        <Route
          path="/actions/:id(\d+)"
          key="action-entity"
          render={props => (
            <Wrapper
              {...props}
              image={bozoAction}
              baseEndpoint="actions"
              component={Entity}
            />
          )}
        />
        <Route
          exact path="/quotes"
          key="quote-home"
          render={props => (
            <Wrapper
              {...props}
              image={bozoQuote}
              baseEndpoint="quotes"
              component={EntityHome}
            />
          )}
        />
        <Route
          path="/quotes/query"
          key="quote-query"
          render={props => (
            <Wrapper
              {...props}
              image={bozoQuote}
              baseEndpoint="quotes"
              component={EntityQuery}
            />
          )}
        />
        <Route
          path="/quotes/:id(\d+)"
          key="quote-entity"
          render={props => (
            <Wrapper
              {...props}
              image={bozoQuote}
              baseEndpoint="quotes"
              component={Entity}
            />
          )}
        />
        ...
      </Switch>
    </Router>
  )
}

沙盒:https://codesandbox.io/s/0y8cm

出于某种原因,沙箱没有重定向并忽略搜索参数,但我们可以通过执行以下操作来评估错误:使用导航栏搜索其中一个实体。检查表单(单击“Mais filtros”)并查看输入中提供的查询参数。在导航栏中再次搜索同一实体。检查表单,看不到初始值。

最佳答案

导致该行为的原因是表单提交事件,它刷新页面导致查询参数丢失,解决方法是提交表单时使用 e.preventDefault() :

const handleSubmit = (e) => {

  e.preventDefault(); // prevent the page refresh

  let baseEndpoint = e.target[1].value === "Ações" ? "actions" : "quotes";
  history.push({
    pathname: `/${baseEndpoint}/query`,
    search: `?description=${e.target[0].value}`
  });
};

如果您绝对需要刷新页面,您可以添加 history.go(),但我不建议这样做,目的是让子组件知道 search 已经改变了,

EntityQueryQueryForm 的设置方式使得子组件无法知道是否有更改,因此您需要修复它,

你不应该在构造函数中设置 props 的初始状态值,因为它是 anti-pattern ,相反,具有初始值 empty 并使用生命周期方法更新它们( componentDidMountcomponentDidUpdate )

实体查询

constructor(props) {
    super(props);
    this.state = {
      error: null,
      isLoaded: true,
      hasMore: false,
      searchParams: {
        page: 1,
        tags: "",
        description: ""
      },
      entities: []
    }
  }

  updateStateValues = () => {
    const _initialTag = this.props.location.search.includes("tags")
      ? this.props.location.search.split("?tags=")[1]
      : "";
    const _initialText = this.props.location.search.includes("description")
      ? this.props.location.search.split("?description=")[1]
      : "";

    this.setState({
      searchParams: {
        page: 1,
        tags: _initialTag,
        description: _initialText
      }
    })
  }

  componentDidMount() {
    this.updateStateValues()
  }

  componentDidUpdate(prevProps) {
    // add checks for other potential props that you need to update if they change
    if (prevProps.location.search.split("?description=")[1] !== this.props.location.search.split("?description=")[1])
      this.updateStateValues()
  }

并将值从 state 传递给 child :

<QueryForm
    baseEndpoint={this.props.baseEndpoint}
    tagsInitialValues={this.state.searchParams.tags}
    textInitialValue={this.state.searchParams.description}
    setSearchParams={this.setSearchParams}
 />

Updated CodePen

编辑:

根据 OP 的评论和更新后的 SandBox,还有另一个关于 Formik 的问题,当 props 更改时,initialValues 不会更新,见:https://github.com/formium/formik/issues/811

建议的添加enableReinitialize 的解决方案不起作用,因此,要强制组件更新,您可以使用一个 key,它会在 url 更改时更改,在在这种情况下,使用 this.props.textInitialValue :

查询表单:

<Formik
  key={this.props.textInitialValue}
  ...

关于reactjs - 如果用户已经在重定向页面上,history.push 会进行额外的重定向,并忽略搜索参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66726644/

相关文章:

javascript - 如何使用 ID/ key (使用 React)从 Firebase 检索和呈现数据?

javascript - React JS 缩短属性

javascript - 在 react 路由器中的路由之间显示一个简单的加载指示器

reactjs - 在 'withRouter' 中找不到导出 'withRouter'(导入为 'react-router-dom')

javascript - 当用户更改输入时,React 不会更新组件状态

reactjs - 如何将 Font Awesome 添加到 Next.js 13 项目错误模块未找到

reactjs - REACT - 在渲染 APP 之前检查身份验证

reactjs - 为什么我需要 redux async thunk

reactjs - react-router-dom 链接在 app.js(父组件)中工作,但在子组件中不起作用

reactjs - 如果我升级react-router-dom,是否会遇到react和react-dom的兼容性问题?