React 编程思想 #2
接上文,已经实现了一个静态的页面,现在就要给页面加上交互了。
寻找 State
状态是应用需要记录的最小变化,构建状态的最重要的原则是 DRY(Don’t Repeat Yourself,不要重复自己)。对于一个应用,构建出它的状态的绝对最小表示,并通过这些状态计算其他需要的内容。例如,如果您正在构建一个购物列表,则可以将项目存储为状态中的数组。如果您还想显示列表中的项数,请不要将项数存储为另一个状态值,而是读取数组的长度。
以上来自 React 的官方文档对状态的定义,简单来说,状态是组件中不可计算的变量,当这些变量改变时,需要动态改变页面的内容,即重新渲染页面。
现在回想示例程序中的所有数据:
- 原始的产品列表;
- 用户输入的搜索文本;
- 单选框的值;
- 经过筛选的产品列表;
这里面哪些属于状态?状态需要满足以下要求:
- 随着时间推移,它会发生改变;
- 它不能是由父组件通过 props 传递来的;
- Don’t Repeat Yourself,它不应该能从任何地方计算出来;
按照这个要求,我们可以进行筛选:
- 原始的产品列表是由 props 传递的,所以它不是状态;
- 用户输入的搜索文本会随着时间改变(用户改变),并且无法根据任何内容进行计算;
- 单选框的值会随着时间改变(用户改变),并且无法根据任何内容进行计算;
- 经过筛选的产品列表可以由原始的产品列表、单选框的值、输入的搜索文本计算出来(筛选),因此它也不是状态;
所以,只有搜索文本和单选框的值才是状态,Nice Done!
React 中有两种类型的“模型”数据:props 和 state。两者区别很大:
props 就像传递给函数的参数。它们允许父组件将数据传递给子组件并自定义其外观。例如,Form 可以将颜色 props 传递给 Button。
state 就像一个组件的内存。它允许组件跟踪一些信息,并根据交互对其进行更改。例如,按钮可能会跟踪 isHovered 状态。
props 和 state 是不同的,但它们是协同工作的。父组件通常会将一些信息保持在 state (可以进行改变),并将其作为子组件的 state 传递给子组件。如果第一次阅读时仍然感觉不清楚,那也没关系。它需要一点练习才能真正坚持下来!
决定 State 的位置
在确定了应用的状态后,还需要确定哪个组件负责拥有和更改这些状态。记住:React使用单向数据流,数据自顶向下流动,数据从父组件向下传递到子组件。目前可能还不清楚哪个组件应该拥有什么状态,如果你是这个概念的新手,这可能很有挑战性,但可以按照以下步骤来解决:
对于应用中的每个状态:
-
识别基于该状态呈现某些内容的每个组件;
-
找到它们最接近的公共父组件————在层次结构中位于它们之上的组件。
-
决定这个组件所在的位置:
通常,可以将状态直接放入它们的公共父级中,也可以将状态放入其公共父级之上的某个组件中。
如果找不到拥有状态的组件,请创建一个仅用于保存状态的新组件,并将其添加到公共父组件上方层次结构中的某个位置。
现在回到这个例子中的状态,决定它们的位置:
识别使用状态的组件:
-
ProductTable 需要根据该状态(搜索文本和单选框的值)筛选产品列表。
-
SearchBar 需要显示该状态(搜索文本和单选框的值)。
找到它们的共同父组件:两个组件共享的第一个父组件是 FilterableProductTable。
决定状态的位置:可以在 FilterableProductTable 中保存搜索文本和单选框的状态值。
因此,状态值将存在于 FilterableProductTable 中,代码如下:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = React.useState('');
const [inStockOnly, setInStockOnly] = React.useState(false);
这里需要提一下,useState
方法只在 React 16.8 版本以上存在,由于我之前使用的是 16.4 版本,这地方一直报错方法不存在,研究了半天才发现问题。
接着,将 State filterText 和 inStockOnly 作为 props 传递给 ProductTable 和 SearchBar:
function FilterableProductTable({ products }) {
const [filterText, setFilterText] = useState('');
const [inStockOnly, setInStockOnly] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly} />
<ProductTable
products={products}
filterText={filterText}
inStockOnly={inStockOnly} />
</div>
);
}
当 filterText 或 inStockOnly 状态改变时(用户进行了操作),拥有这些状态的组件 FilterableProductTable 会感知到状态的变化,重新渲染页面内容;又因为 ProductTable 和 SearchBar 组件在 FilterableProductTable 组件中,同时接收了这两个状态作为 props,它们也会重新渲染。这样,ProductTable 和 SearchBar 就可以根据状态更改自身显示的内容了。
function ProductTable({ products, filterText, inStockOnly }) {
const rows = [];
let lastCategory = null;
products.forEach((product) => {
if (
product.name.toLowerCase().indexOf(
filterText.toLowerCase()
) === -1
) {
return;
}
if (inStockOnly && !product.stocked) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category} />
);
}
rows.push(
<ProductRow
product={product}
key={product.name} />
);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function SearchBar({ filterText, inStockOnly }) {
return (
<form>
<input
type="text"
value={filterText}
placeholder="Search..."/>
<label>
<input
type="checkbox"
checked={inStockOnly} />
{' '}
Only show products in stock
</label>
</form>
);
}
其中,SearchBar 组件根据这两个状态,更改页面上显示的内容;ProductTable 组件根据这两个状态,重新计算经过筛选的产品列表,如此便实现了页面的动态变化。
添加反向数据流
目前,应用已经通过自顶向下流动的 props 和 state 进行了正确的渲染。但是,要想根据用户输入更改状态,还需要支持数据以另一种方式流动:位于层次结构中下方的表单组件需要更新 FilterableProductTable 中的状态。
React明确地规定了数据流动的方式,但相比于双向数据绑定,它需要更多的输入(代码编写)。如果尝试在上面的组件构成的页面中输入搜索内容或改变单选框,可以看到输入没有响应,被 React 忽略了。这是故意的(
标签:状态,product,编程,思想,FilterableProductTable,React,组件,filterText,SearchBar From: https://www.cnblogs.com/qiyuanc/p/Front7.html