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

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

内容简介:原文:作者: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▽ ̄)~*捂嘴偷笑

希望对你有帮助!

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


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

查看所有标签

猜你喜欢:

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

The Haskell School of Expression

The Haskell School of Expression

Paul Hudak / Cambridge University Press / 2000-01 / USD 95.00

Functional programming is a style of programming that emphasizes the use of functions (in contrast to object-oriented programming, which emphasizes the use of objects). It has become popular in recen......一起来看看 《The Haskell School of Expression》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具