【译】在 React 中处理状态的替代方法:URL

栏目: IOS · Android · 发布时间: 5年前

内容简介:原文:作者:GaelS译者:博轩

原文: An alternative to handle state in React: the URL !

作者:GaelS

译者:博轩

【译】在 React 中处理状态的替代方法:URL

React App 中的状态

如何在 React App 中管理全局状态,是所有类库之间一直争论不休的事情。然而,依我拙见,我们使用 URLreact-router 也可以做同样的事情。

URL的胜利(FTW)

在单页面应用中, URL 并不重要。大多数情况下,它只是一个请求所有资源的站点。

当你访问 https://myApp.io ,还是访问 https://myApp.io?user=gael&jo... 时,你第一次访问页面所看到的信息都是一样的。

让我们来解决这个问题。

译注:由于国内被墙了,不能直接访问 https://myApp.io 。我找了一个单页面应用: https://www.souche.com 。就是希望从首页输入的查询条件,页面跳转之后,会出现在地址栏,并且页面的状态(查询输入框,分页条件)会和地址栏中保持一致。

示例代码

一开始,我在 first-contrib-app 项目中使用了这个想法。( 代码 ,以及 演示

但是,为了这篇文章,我重新在 codesandbox 上面制作了一个 示例 ,来专注于这个问题的解决方案。

首先,我们将如何使用 URL ?

我们将使用 URL 中, ? 后面所包含的所有内容,就是所谓的 搜索参数

搜索参数的 MDN 链接

从 URL 中获取信息

在本文的上下文中,我们将只使用一个查询参数: query

为了收集该参数(如果它确实存在于 URL,例如 https://myApp.io?query=javascript ),我们将会检查 搜索参数 。幸运的是,他们可以在 window 对象中很容易找到。更准确的说,是 winndow.location.search

因此,当我们访问 www.first-contrib?query=react 的使用,我们在控制台打印会得到:

console.log(window.location.search); // "?query=react"

在理想情况下,格式化后的 JS 对象,会比字符串更加方便理解。为了实现这一点,我们将使用浏览器的最新 API URLSearchParams 对象,而不是分割 URL 中的 =? 。除此之外,同样可以使用 URLSearchParamspolyfill 版本

代码如下:

function getParams(location) {
  const searchParams = new URLSearchParams(location.search);
  return {
    query: searchParams.get('query') || '',
  };
}

因此,我们可以这样使用:

const params = getParams('www.first-contrib.fr?query=react');

console.log(params) // { query: "react" }

现在,我们可以从 URL 中获取一个参数对象,接下来将结合 react-router ,在我们的应用中使用。因此,我们将创建一个 router 来处理路由,并从 props 中获取 route 属性。

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

// ...
// getParams code above

//a simple component to display 
//the value of the query ...
// which is for now unknown
//so we'll instantiate it with an empty value
const MainPage = (props) => {
  let query = '';
  return (
    <h2>{`Query : ${query}`}</h2>
  );
}

const App = () => (
  <React.Fragment>
    <Router>
      <React.Fragment>
        <Route path="/" component={MainPage} />
      </React.Fragment>
    </Router>
  </React.Fragment>
);

render(<App />, document.getElementById("root"));

为了获取查询参数: query 的实际值,我们将使用 getParams 函数,在 MainPage 组件中,处理 从 props 中获取的 Route 对象:

<Route path="/" component={MainPage} />

如果我们打印 props ,我们将会得到:

{match: Object, location: Object, history: Object, /*other stuff */}

有趣的是,这里的 location 对象,和之前的 window.location 结构很相似,这样,我们操作会更简单。因此,我们可以更新 MainPage 组件,让他可以从 URL 中获取值。

const MainPage = (props) => {
  const { location } = props;
  const { query } = getParams(location);

  return (
      <h2>{`My query: ${query}`}</h2>
  );
}

现在, MainPage 可以使用 URL 了!

更新 URL (以及状态)

现在,我们可以从 URL 中获取信息,我们将实现一种方法,根据应用程序的状态,来更新 URL。

为此,我准备了一个简单的输入框示例:

class InputPage extends React.Component {

  state = { inputValue: "" };

  updateInputValue = e => this.setState({ inputValue: e.target.value });

  render() {
    return (
      <React.Fragment>
        <input
          type="text"
          placeholder="Change your URL !"
          value={this.state.inputValue}
          onChange={this.updateInputValue}
        />
        <input type="button" value="Change the URL" onClick={null} />
      </React.Fragment> 
    );
  }
}

到目前为止,我们的组件编辑内部状态,来展示其当前的值。但是,我们仍然必须实现 onClick 函数来更新 URL,即使是相同的查询参数。

我们可以看到从 Route 传过来的 props 对象展示如下:

{match: Object, location:Object, history: Object, /*d'autres valeurs */}

这里,我们关心的是 history 对象(有关 history 对象的其他信息在 这里 ...)

ReactRouter 文档中, push 函数的示意如下:

将新的输入,推送到历史的堆栈当中

简单来说,我们可以使用 push 方法来更新 URL !

因此,如果我们输入的查询条件是 javascript ,我们必须使用 www.myApp.io?query=javascript 来更新 URL 。因此,我们需要为 URL 生成新的查询参数。为了实现这一目标, URLSearchParams 对象将再一次帮到我们。

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

请注意,当查询参数: query 未定义,而且没有默认值的时候,生成的 URL 将会是 ?query=undefined...

现在我们可以这样写:

const url = setParams({ query: "javascript" });
console.log(url); // "query=javascript"

我们可以在输入组件中实现 onClick

class InputPage extends React.Component {
  state = { inputValue: "" };
  updateInputValue = e => this.setState({ inputValue: e.target.value });

  updateURL = () => {
    const url = setParams({ query: this.state.inputValue });
    //do not forget the "?" !
    this.props.history.push(`?${url}`);
  };

  render() {
    return (
      <React.Fragment>
          <input
          type="text"
          className="input"
          placeholder="What am I looking for ?"
          value={this.state.inputValue}
          onChange={this.updateInputValue}
        />
        <input
          type="button"
          className="button"
          value="Update the URL !"
          onClick={this.updateURL}
        />
      </React.Fragment> 
    );
  }
}

现在,如果我们更改输入的值,单击按钮我们将触发 URL 的更新, MainPage 将相应地显示新的值。

将应用程序的状态保存在 URL 当中,最大的优势在于当你复制,粘贴链接的时候。由于状态包含在 URL 当中,我们的应用程序在首次加载的时候,将会保持这个状态。

例如,当您在处理搜索引擎的时候,您可以在加载应用程序后立即触发查询。在这个 first-contrib 应用 中,我使用 react-apollo 很轻松的实现了。但是同样,我们可以使用任何 HTTP 客户端来实现相同的功能。

让我们创建一个组件,使用 axios 处理请求,以及 Github REST API (不需要任何登录认证),使用一些生命周期方法来获取 props

const httpClient = axios.create({
  baseURL: "https://api.github.com"
});

class ResultsPage extends React.Component {
  state = { results: [], loading: false, error: false };

  //Search as soon as it is mounted !!
  componentDidMount() {
    return this.searchRepositories(this.props.query);
  }

  //Search as soon as query value is updated
  componentWillReceiveProps(nextProps) {

    if (nextProps.query !== this.props.query) {
      this.setState({ query: nextProps.query });
      return this.searchRepositories(nextProps.query);
    }
  }

  searchRepositories = query => {

    //handle if query is undefined
    if (!query) {
      return this.setState({
        results: []
      });
    }

    this.setState({ loading: true, error: false });

    //the actual search on Github
    return httpClient
      .get(`/search/repositories?q=${query}`)
      .then(({ data }) =>
        this.setState({
          results: data.items,
          loading: false
        })
      )
      .catch(e => this.setState({ loading: false, error: true }));
  };


  render() {
    return (
      <div>
        {this.state.results.map(repo => (
          <div key={repo.id}>
            <a href={repo.html_url}>
              {repo.name}
            </a>
            <div>{`by ${repo.owner.login}`}</div>
          </div>
        ))}
      </div>
    );
  }
}

就如同我们所看到的,我们现在有一个组件,只要更新 URL 中的查询参数,就会触发请求!

示例链接

在我们的示例中,它只能处理一个名为 query 的查询参数,但是如果很多组件都可以来更新 URL 的状态,这个用法将变得更加强大。例如,分页,过滤,排序等也可以生成 URL 的参数。链接会是这个样子: https://myApp.io?query=react&sort=ASC&filter=issues&page=2

代码与我们之前的代码类似。通过修改 URL ,可以更新 Route 组件所提供的 props 。然后,通过监听 URL 中的特殊值,会触发自身以及子组件的重新渲染。因此,它会使 UI 更新,以及触发副作用,例如 HTTP 请求。

总结

就是这样!这篇文章向您展示了在 React 应用中,一种处理全局状态的备选方案。就包管理而言,它很轻(在现代浏览器中只有 0 KB ( '▽' )♪),使用,简单,并可以为应用带来,直接可以访问深层链接的效果,我觉得这很酷。 ( ̄y▽ ̄)~*捂嘴偷笑

希望对你有帮助!

译注:我偷偷改了作者原来的颜文字...


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

亿级流量网站架构核心技术

亿级流量网站架构核心技术

张开涛 / 电子工业出版社 / 2017-4 / 99

《亿级流量网站架构核心技术》一书总结并梳理了亿级流量网站高可用和高并发原则,通过实例详细介绍了如何落地这些原则。本书分为四部分:概述、高可用原则、高并发原则、案例实战。从负载均衡、限流、降级、隔离、超时与重试、回滚机制、压测与预案、缓存、池化、异步化、扩容、队列等多方面详细介绍了亿级流量网站的架构核心技术,让读者看后能快速运用到实践项目中。 不管是软件开发人员,还是运维人员,通过阅读《亿级流......一起来看看 《亿级流量网站架构核心技术》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具