内容简介:原文:作者:GaelS译者:博轩
原文: An alternative to handle state in React: the URL !
作者:GaelS
译者:博轩
React App 中的状态
如何在 React App
中管理全局状态,是所有类库之间一直争论不休的事情。然而,依我拙见,我们使用 URL
和 react-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 中, ? 后面所包含的所有内容,就是所谓的 搜索参数 。
从 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 中的 =
和 ?
。除此之外,同样可以使用 URLSearchParams
的 polyfill
版本 。
代码如下:
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▽ ̄)~*捂嘴偷笑
希望对你有帮助!
译注:我偷偷改了作者原来的颜文字...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Async Generators 作为状态管理的替代方案
- [译] Async Generators 作为状态管理的替代方案
- Postwoman 替代 Postman
- JSX的替代品[译]
- [译]JSX的替代品
- fetch使用,ajax替代方案
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编程珠玑(第2版•修订版)
[美] Jon Bentley 乔恩•本特利 / 黄倩、钱丽艳 / 人民邮电出版社 / 2014-12 / 39
历史上最伟大的计算机科学著作之一 融深邃思想、实战技术与趣味轶事于一炉的奇书 带你真正领略计算机科学之美 多年以来,当程序员们推选出最心爱的计算机图书时,《编程珠玑》总是位于前列。正如自然界里珍珠出自细沙对牡蛎的磨砺,计算机科学大师Jon Bentley以其独有的洞察力和创造力,从磨砺程序员的实际问题中凝结出一篇篇不朽的编程“珠玑”,成为世界计算机界名刊《ACM通讯》历史上最受欢......一起来看看 《编程珠玑(第2版•修订版)》 这本书的介绍吧!