首页 > 解决方案 > 如果用户已经在重定向页面上,history.push 正在执行额外的重定向,并忽略搜索参数

问题描述

我有一个简单NavbarForm里面有一个:

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}`, 
    })
}

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

非常感谢!

(编辑)我SwitchRoutes:

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”)并在输入处查看提供的查询参数。在导航栏中对同一实体进行另一次搜索。检查表格,看不到初始值。

标签: reactjsreact-routerreact-router-dom

解决方案


导致该行为的原因是表单提交事件,它刷新页面导致查询参数丢失,解决方案是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设置方式使子组件无法知道是否有更改,因此您需要修复它,

您不应该从构造函数中的道具设置初始状态值,因为它是反模式的,而是具有初始值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}
 />

更新了 CodePen

编辑:

跟进 OP 的评论和更新的 SandBox,还有一个关于更改FormikinitialValues不更新的props问题,请参阅:https ://github.com/formium/formik/issues/811

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

查询表格:

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

推荐阅读