如何使用输入字段更新页面的 url?

2023-12-08

我尝试将搜索页面与 React Router v5 集成到我的应用程序中。

如何使用搜索框更新网址的查询参数?

当我刷新应用程序时,我会丢失搜索结果和搜索字段的值。

我使用 redux 来管理搜索字段和搜索结果的值的状态,我认为遍历 url 的参数将是一个更好的解决方案,但我不知道该怎么做。

我尝试了一个解决方案(参见我的代码),但是url的查询参数与我的文本字段的值不同步

Edit:

我的组件 Routes.js

 const Routes = (props) => {
    return (
       <div>
          <Route exact path="/" component={Home} />
          <Route
             exact
             path='/search'
             component={() => <Search query={props.text} />}
          />
          <Route path="/film/:id" component={MovieDetail} />  
          <Route path="/FavorisList" component={WatchList} />
          <Route path="/search/:search" component={Search} />
          <Route path="*" component={NotFound} />  
       </div>

  )}

我的组件SearchBar.js(嵌入在导航栏中,搜索路由显示搜索结果)

EDIT:

我希望实现Netflix用于剧集研究的方法。

我希望能够搜索,无论我在哪个页面,如果输入字段中有一个条目,我会导航到search页面与this.props.history.push (`` search /),如果输入字段为空,我将导航到带有this.props.history.goBack ().

状态 inputChange 是一个标志,可防止我每次输入字符时推送到搜索页面。

要了解更多信息,我在这里开了=一篇文章=>如何根据字段值改变路由

  class SearchBar extends React.Component {
      constructor(props) {
         super(props);

         this.state = {
            inputValue:'',
         };

       }

      setParams({ query }) {
         const searchParams = new URLSearchParams();
         searchParams.set("query", query || "");
         return searchParams.toString();
      }

      handleChange = (event) => {

          const query = event.target.value
          this.setState({ inputValue: event.target.value})

          if (event.target.value === '') {
             this.props.history.goBack()
             this.setState({ initialChange: true }) 
             return;
          } 

          if(event.target.value.length && this.state.initialChange){
               this.setState({
                  initialChange:false
               }, ()=> {

              const url = this.setParams({ query: query });
              this.props.history.push(`/search?${url}`)
              this.search(query)
           })
         }
       }

       search = (query) => {
           //search results retrieved with redux 
           this.props.applyInitialResult(query, 1)
       }

       render() {
          return (
            <div>
               <input
                   type="text"
                   value={this.state.inputValue}
                   placeholder="Search movie..."
                   className={style.field}
                   onChange={this.handleChange.bind(this)}
               />  
            </div>
          );
        }


       export default SearchBar;

组件App.js

       class App extends React.Component {
          render(){
              return (
                 <div>
                    <BrowserRouter>
                       <NavBar />
                       <Routes/>
                    </BrowserRouter>
                 </div>
              );
           }
        }

        export default App;

查询搜索结果(使用 Redux 管理)

      export function applyInitialResult(query, page){
          return function(dispatch){
              getFilmsFromApiWithSearchedText(query, page).then(data => {
                 if(page === 1){
                     dispatch({
                        type:AT_SEARCH_MOVIE.SETRESULT,
                        query:query,
                        payload:data,
                     })
                  }
               })
             }
         }

您可以只使用可选参数并通过更改来处理组件中的查询或缺少查询,而不是分割路由<Route path="/search/:search" component={Search} /> to <Route path="/search/:search?" component={Search} />并删除<Route exact path='/search' component={() => <Search query={props.text} />} />完全。

进行此更改后,您可以通过查看值来获取当前查询props.match.params.search在这个组件中。由于每次用户更改输入时您都会更新 URL,因此您无需担心在组件状态下管理它。此解决方案的最大问题是您可能希望在渲染后延迟一点搜索,否则您将在每次击键时触发调用。

编辑以回应问题更新

你是对的,如果 applyInitialResult 只是一个动作创建者,它不会是异步的或thenable的。不过,你仍然有选择。

例如,您可以更新操作创建器,以便它接受回调来处理数据获取的结果。我还没有测试过这个,所以将其视为伪代码,但这个想法可以这样实现:

动作创造者

   export function applyInitialResult(
      query, 
      page,
      // additional params
      signal,
      onSuccess,
      onFailure
      // alternatively, you could just use an onFinished callback to handle both success and failure cases
   ){
      return function(dispatch){
          getFilmsFromApiWithSearchedText(query, page, signal) // pass signal so you can still abort ongoing fetches if input changes
             .then(data => {
                onSuccess(data); // pass data back to component here
                if(page === 1){
                    dispatch({
                       type:AT_SEARCH_MOVIE.SETRESULT,
                       query:query,
                       payload:data,
                    })
                 }
              })
              .catch(err => {
                  onFailure(data); // alert component to errors
                  dispatch({
                     type:AT_SEARCH_MOVIE.FETCH_FAILED, // tell store to handle failure
                     query:query,
                     payload:data,
                     err
                  })
              })
          }
      }

搜索电影减速器:

// save in store results of search, whether successful or not
export function searchMovieReducer(state = {}, action) => {
   switch (action.type){
      case AT_SEARCH_MOVIE.SETRESULT:
         const {query, payload} = action;
         state[query] = payload;
         break;
      case AT_SEARCH_MOVIE.FETCH_FAILED:
         const {query, err} = action;
         state[query] = err;
         break;
   }
}

然后,您仍然可以在触发获取操作的组件中直接获得结果/错误。虽然您仍然可以通过商店获取结果,但您可以使用这些触发器来管理initialChange在组件状态中以避免冗余动作调度或在这些情况下可能出现的无限循环。

在这种情况下,您的搜索栏组件可能如下所示:

class SearchBar extends React.Component {
    constructor(props){

       this.controller = new AbortController();
       this.signal = this.controller.signal;

       this.state = {
           fetched: false,
           results: props.results // <== probably disposable based on your feedback
       }
    }

    componentDidMount(){
        // If search is not undefined, get results
        if(this.props.match.params.search){
            this.search(this.props.match.params.search);
        }
    }

    componentDidUpdate(prevProps){
        // If search is not undefined and different from prev query, search again
        if(this.props.match.params.search
          && prevProps.match.params.search !== this.props.match.params.search
        ){
            this.search(this.props.match.params.search);
        }
    }

    setParams({ query }) {
       const searchParams = new URLSearchParams();
       searchParams.set("query", query || "");
       return searchParams.toString();
    }

    handleChange = (event) => {
       const query = event.target.value
       const url = this.setParams({ query: query });
       this.props.history.replace(`/search/${url}`);
    }

    search = (query) => {
        if(!query) return; // do nothing if empty string passed somehow
        // If some search occurred already, let component know there's a new query that hasn't yet been fetched
        this.state.fetched === true && this.setState({fetched: false;})

        // If some fetch is queued already, cancel it
        if(this.willFetch){
            clearInterval(this.willFetch)
        }

        // If currently fetching, cancel call
        if(this.fetching){
            this.controller.abort();
        }

        // Finally queue new search
        this.willFetch = setTimeout(() => {
            this.fetching = this.props.applyInitialResult(
                query,
                1,
                this.signal,
                handleSuccess,
                handleFailure
            )
        },  500 /* wait half second before making async call */);
    }

    handleSuccess(data){
       // do something directly with data
       // or update component to reflect async action is over
    }

    handleFailure(err){
       // handle errors
       // or trigger fetch again to retry search
    }

    render() {
       return (
         <div>
            <input
                type="text"
                defaultValue={this.props.match.params.search} // <== make input uncontrolled
                placeholder="Search movie..."
                className={style.field}
                onChange={this.handleChange.bind(this)}
            />  
         <div>
       );
    }
}

const mapStateToProps = (state, ownProps) => ({
   // get results from Redux store based on url/route params
   results: ownProps.match.params.search
       ? state.searchMovie[ownProps.match.params.search]
       : []
});

const mapDispatchToProps = dispatch => ({
   applyInitialResult: // however you're defining it
})

export default connect(
   mapStateToProps,
   mapDispatchToProps
)(SearchBar)

EDIT 2

感谢您对您的想象进行澄清。

原因this.props.match.params始终为空,因为它仅适用于搜索组件 - 搜索栏完全位于路由设置之外。它还渲染当前路径是否是/search/:search, 这就是为什么withRouter没工作。

另一个问题是您的搜索路线正在寻找该匹配参数,但您正在重定向到/search?query=foo, not /search/foo,因此搜索上的匹配参数也将为空。

我还认为你的管理方式initialChangestate 是导致您的搜索值保持不变的原因。您的处理程序会在输入的每个更改事件上被调用,但它会在第一次击键后自行关闭,并且在清除输入之前不会再次打开。看:

      if (event.target.value === '') {
         this.props.history.goBack()
         this.setState({ initialChange: true }) // <== only reset in class
         return;
      } 
      ...
      if(event.target.value.length && this.state.initialChange){
           this.setState({
              initialChange:false
           }, ()=> {
           // etc...
       })
     }

这就是我建议的模式要实现的目标 - 不要立即关闭处理程序,而是为调度设置延迟并继续侦听更改,仅在用户完成输入后进行搜索。

我没有在这里复制另一个代码块,而是在codesandbox上制作了一个工作示例here解决这些问题。它仍然可以使用一些优化,但是如果您想查看我如何处理搜索页面、搜索栏和操作创建器,那么这个概念就在那里。

搜索栏还有一个切换开关,可以在两种不同的 url 格式之间切换(查询如/search?query=foo与 match.param 类似/search/foo)这样您就可以了解如何将每一项协调到代码中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用输入字段更新页面的 url? 的相关文章

随机推荐

  • 使用 CSS 将 pre 元素另存为 PDF

    我制造了一个语法荧光笔我想要一个保存为 PDF 的选项 我看过这个问题 但下载它不会保留 CSS 样式 这会破坏下载突出显示文件的意义 有什么办法可以拯救我的pre元素作为 PDF 同时保留 CSS HTML pre highlighted
  • 如何在列表中放置列表项项目符号?

    你会看到我的 HTML 生成的数字 项目符号使左侧的元素看起来很难看 https i stack imgur com nAb8C jpg 我试图在 CSS 中使用边距和填充 但两者都不起作用 帮助 我希望数字显示距图像左侧至少 5 像素 A
  • Woocommerce:用户只能购买一次产品

    我可以在主题的functions php 文件中添加什么 以使用户只能购买产品一次 就像他们以前在商店购买过任何产品一样 他们将无法再次购买 我想代码看起来像这样 伪代码 并且会使用 woocommerce add cart item da
  • JSON 解析错误:意外的标识符“未定义”(React Native)

    我正在尝试使用异步存储来存储两个作为对象存储的文本输入值 并将其传递到不同的视图 在按下按钮时它们将显示在其中 正如其他 StackOverflow 帖子中提到的 我使用 JSON parse 和 JSON stringify 将对象作为
  • Xcode 签名错误无法创建配置文件并且未找到“com.photo.appwall”的配置文件

    我在签署申请时遇到问题 无法创建配置文件 未找到 com photo appwall 的配置文件 该错误消息似乎不言自明 您需要在 Apple 的开发人员门户上注册您的测试设备 登录 Apple 的开发者门户 Click Certifica
  • 如何在 Node.js 中从 URL 进行请求

    是否有标准方法要求 Node 模块位于某个 URL 而不是本地文件系统上 就像是 require http example com nodejsmodules myModule js 目前 我只是将文件提取到临时文件中 并要求这样做 您可以
  • Windows 资源监视器使用哪个 API?

    Windows 资源监视器显示 除其他外 当前哪些进程正在访问磁盘上的哪些文件 它是实时进行的 How 我知道它可能使用 ETW 并且我可以使用 xperf 等工具生成跟踪 但是如何在无需启动 停止和解析跟踪文件的情况下获取实时信息呢 我需
  • 在Android中向多列GridView添加页脚视图?

    是否可以将页脚视图添加到 GridView 具有多列 其行为类似于 ListView 的页脚 那么这个页脚视图 例如分页视图 仅在用户滚动到 GridView 底部时才会出现 并且它具有整个屏幕的宽度 而不仅仅是 1 个网格元素 不 对不起
  • “num - 1”与“num -= 1”

    在第 4 行中 为什么我们必须在 后面添加 num 5 if num gt 2 print num num 1 print num num 1 产生减去 1 的结果num num没有改变 num 1 减一num并存储该结果 相当于num n
  • 小程序无法从 jar 加载类

    一些用户抱怨小程序不再工作 当他们查看 java 控制台时 他们会遇到 java lang noClassDefFoundError 并检查我的访问日志 我发现他们已经下载了包含该类的 jar 文件 然后发出 get对特定类别的请求 不同的
  • 如何用图像中每个像素的颜色绘制图形?

    我正在研究图像颜色识别 因此我将 RGB 图像转换为 Lab 因为它是最接近人类视觉的颜色空间 之后 我获取实验室的 3 个通道中的每一个 并希望在 3D 图形中绘制我在转换图像中识别出的颜色变化 如何使用图像的颜色绘制图形 import
  • iphone MGTwitterEngine - 使用我的应用程序签名发布推文

    我在 iPhone 应用程序中使用 MGTwitterEngine 效果非常好 我想要更改的一件事是 Twitter 上出现的 从 MGTwitterEngine 发布 显然 我希望它说 来自 MyCoolApp 并链接到该应用程序的网站
  • 特使过滤器拦截上游响应

    我已经为 envoy 编写了 ext authz 过滤器 并且对 envoy 过滤器的工作原理有基本的了解 但现在我想过滤从上游返回的响应 具体来说 我想处理两件事 在 Envoy 发送回下游之前 拦截来自上游的 data jsonBody
  • jquery验证插件,如何在自定义方法中添加多个自定义消息

    我在用jquery 验证插件 我使用添加了一个自定义方法添加方法这又调用另一个方法来检查是否有效UK telephone number 这是我的代码 简化 html
  • Apache PDFBOX - 使用 split(PDDocument 文档)时出现 java.lang.OutOfMemoryError

    我正在尝试使用 Apache PDFBOX API V2 0 2 拆分一个 300 页左右的文档 尝试使用以下代码将 pdf 文件拆分为单页时 PDDocument document PDDocument load inputFile Sp
  • 在 D3 中通过上下文缩放和画笔移动散点图圆圈

    我正在尝试基于 d3 示例创建 Focus Context Tooltip 图http bl ocks org 1667367 我已经使基本图表正常工作 但是当我尝试使用焦点图放大某个区域时 我计划用于工具提示的 圆圈 不会移动 这是我的代
  • IOS:使用图案图像作为背景-内存泄漏

    好的 我会寻找答案 也许我自己就能找到 我有一个坏习惯 就是自己回答问题 无论如何 我有一个设计为相当容易 换肤 的应用程序 作为其中的一部分 我在特定于变体的静态类中隔离了方法 这些静态方法为主应用程序提供特定于变体的图像 颜色和设置 h
  • 为什么 `False in pandas.Series([True,True])` 返回 True?

    False in True True False in pd Series True True 第一行代码返回False 第二行代码返回True 我想我一定是在这里做错了什么或者错过了什么 当我检查该系列是否为 0 时 我得到了同样的结果
  • 无法下载jmeter插件:Json Path Extractor

    我无法下载 apache jmeter Json Path Extractor 加载器保持加载 url https jmeter plugins org search jpgc json 安装 JMeter 插件的最佳方法是使用插件管理器
  • 如何使用输入字段更新页面的 url?

    我尝试将搜索页面与 React Router v5 集成到我的应用程序中 如何使用搜索框更新网址的查询参数 当我刷新应用程序时 我会丢失搜索结果和搜索字段的值 我使用 redux 来管理搜索字段和搜索结果的值的状态 我认为遍历 url 的参