この記事で共有する内容は、Paste.js レスポンシブ フレームワークの配列レンダリングと操作に関するもので、必要な友人は参考にしてください。これは Pastate.js レスポンシブな反応状態管理フレームワークです。シリーズのチュートリアルの第 3 章、注目して更新を続けてください。
この章では、ペースト状態の配列をレンダリングして処理する方法を見てみましょう。
配列のレンダリング
const initState = { basicInfo: ..., address: ..., pets: [{ id:'id01', name: 'Kitty', age: 2 }] }
オブジェクト要素で構成される配列 initState.pets を定義します。この配列には初期要素があります。
次に、ペットの値を表示するための関連コンポーネントを定義します。
class PetsView extends PureComponent { render() { /** @type {initState['pets']} */ let state = this.props.state; return ( <p style={{ padding: 10, margin: 10 }}> <p><strong>My pets:</strong></p> {state.map(pet => <PetView state={pet} key={pet.id}/>)} </p> ) } }
class PetView extends PureComponent { render() { /** @type {initState['pets'][0]} */ let state = this.props.state; return ( <p> <li> {state.name}: {state.age} years old.</li> </p> ) } }
ここでは 2 つのコンポーネントが定義されています。1 つ目は PetsView で、ペット配列の表示に使用されます。2 つ目は PetView で、ペット要素の表示に使用されます。
次に、PetsView コンポーネントを AppView コンポーネントに配置して表示します:... class AppView extends PureComponent { render() { /** @type {initState} */ let state = this.props.state; return ( <p style={{ padding: 10, margin: 10, display: "inline-block" }}> <BasicInfoView state={state.basicInfo} /> <AddressView state={state.address} /> <PetsView state={state.pets} /> </p> ) } } ...
配列を変更します
を強化しました。これらの配列関数を直接呼び出すことができ、paste はビューの更新を自動的にトリガーします。これら 7 つの配列変更メソッドは次のとおりです
push()
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
我们来尝试使用 push 和 pop 来更新数组:
class PetsView extends PureComponent { pushPet(){ state.pets.push({ id: Date.now() + '', name: 'Puppy', age: 1 }) } popPet(){ state.pets.pop() } render() { /** @type {initState['pets']} */ let state = this.props.state; return ( <p style={{ padding: 10, margin: 10 }}> <p><strong>My pets:</strong></p> {state.map(pet => <PetView state={pet} key={pet.id}/>)} <p> <button onClick={this.pushPet}>push pet</button> <button onClick={this.popPet}>pop pet</button> </p> </p> ) } }
非常容易!我们还添加了两个按钮并指定了点击处理函数,运行体验一下:
打开 react dev tools 的 Highlight Updates 选项,并点击 push 或 pop 按钮,可以观察到视图更新情况如我们所愿:
通常情况下,数组节点的初始值是空的。为了实现编辑器 intelliSence, 我们可以在外面定义一个元素类型,并注释这个数组节点的元素为该类型:
const initState = { ... /** @type {[pet]} */ pets: [] } const pet = { id: 'id01', name: 'Kitty', age: 2 }
你也可以使用泛型的格式来定义数组类型: /** @type {Array<pet>} */
。
上一章我们提到了单实例组件,是指组件只被使用一次;而我们可以到 PetView 被用于显示数组元素,会被多次使用。我们把这类在多处被使用的组件称为多实例组件。多实例组件内部动作的处理逻辑由组件实例的具体位置而定,与单实例组件的处理模式有差别,我们来看看。
我们试着制作一个每个宠物视图中添加两个按钮来调整宠物的年龄,我们用两种传统方案和pastate方案分别实现:
父组件向子组件传递绑定index的处理函数:这种模式是把子组件的动作处理逻辑实现在父组件中,然后父组件把动作绑定对应的 index 后传递给子组件
class PetsView extends PureComponent { ... addAge(index){ state.pets[index].age += 1 } reduceAge(index){ state.pets[index].age -= 1 } render() { /** @type {initState['pets']} */ let state = this.props.state; return ( <p style={{ padding: 10, margin: 10 }}> ... { state.map((pet, index) => <PetView state={pet} key={pet.id} addAge={() => this.addAge(index)} // 绑定 index 值,传递给子组件 reduceAge={() => this.reduceAge(index)} // 绑定 index 值,传递给子组件 />) } ... </p> ) } }
class PetView extends PureComponent { render() { /** @type {initState['pets'][0]} */ let state = this.props.state; return ( <p > <li> {state.name}: <button onClick={this.props.reduceAge}> - </button> {/* 使用已绑定 index 值得动作处理函数 */} {state.age} <button onClick={this.props.addAge}> + </button> {/* 使用已绑定 index 值得动作处理函数 */} years old. </li> </p> ) } }
这种模式可以把动作的处理统一在一个组件层级,如果多实例组件的视图含义不明确、具有通用性,如自己封装的 Button 组件等,使用这种动作处理模式是最好的。但是如果多实例组件的含义明显、不具有通用性,特别是用于显示数组元素的情况下,使用这种模式会引发多余的渲染过程。
打开 react dev tools 的 Highlight Updates 选项,点击几次 push pet
增加一些元素后,再点击 +
或 -
pop()
shift ( )
unshift()
🎜splice()
🎜🎜🎜 sort ()
🎜🎜🎜reverse()
🎜🎜プッシュとポップを使って配列を更新してみましょう: 🎜class PetsView extends PureComponent { ... render() { ... return ( <p style={{ padding: 10, margin: 10 }}> ... { state.map((pet, index) => <PetView state={pet} key={pet.id} index={index} // 直接把 index 值传递给子组件 />) } ... </p> ) } }
class PetView extends PureComponent { // 在子组件实现动作逻辑 // 调用时传递 index addAge(index){ state.pets[index].age += 1 } // 或函数自行从 props 获取 index reduceAge = () => { // 函数内部使用到 this 对象,使用 xxx = () => {...} 来定义组件属性更方便 state.pets[this.props.index].age -= 1 } render() { /** @type {initState['pets'][0]} */ let state = this.props.state; let index = this.props.index; return ( <p > <li> {state.name}: <button onClick={() => this.reduceAge(index)}> - </button> {/* 使用闭包传递 index 值 */} {state.age} <button onClick={this.addAge}> + </button> {/* 或让函数实现自己去获取index值 */} years old. </li> </p> ) } }
/** @type {配列<pet>}*/
。 🎜🎜マルチインスタンスコンポーネントの内部アクション処理🎜🎜前の章で説明しました🎜単一インスタンスコンポーネント🎜。これは、コンポーネントが一度だけ使用されることを意味し、PetView は配列要素を表示するために使用されることがわかります。複数回使用されました。このように複数の場所で使用されるコンポーネントを🎜マルチインスタンス コンポーネント🎜と呼びます。マルチインスタンス コンポーネントの内部アクションの処理ロジックは、コンポーネント インスタンスの特定の場所によって決まります。これは、単一インスタンス コンポーネントの処理モードとは異なります。見てみましょう。 🎜🎜ペットの年齢を調整するために各ペット ビューに 2 つのボタンを追加する方法を作成しようとします。2 つの従来のソリューションとペースト ソリューションをそれぞれ使用して実装します。 🎜class PetsView extends PureComponent { ... render() { ... return ( <p style={{ padding: 10, margin: 10 }}> ... { state.map((pet, index) => <PetView state={pet} key={pet.id} {/* 注意,这里无需传递 index 值,除非要在子组件中有其他用途*/} />) } ... </p> ) } }
import {..., getResponsiveState } from 'pastate' class PetView extends PureComponent { addAge = () => { /** @type {initState['pets'][0]} */ let pet = getResponsiveState(this.props.state); // 使用 getResponsiveState 获取响应式 state 节点 pet.age += 1 } reduceAge = () => { /** @type {initState['pets'][0]} */ let pet = getResponsiveState(this.props.state); // 使用 getResponsiveState 获取响应式 state 节点 pet.age -= 1 } render() { /** @type {initState['pets'][0]} */ let state = this.props.state; return ( <p > <li> {state.name}: <button onClick={this.reduceAge}> - </button> {state.age} <button onClick={this.addAge}> + </button> years old. </li> </p> ) } }
push pet
を数回クリックし、いくつかの要素を追加した後、+
または -
ボタンをクリックします。コンポーネントの再レンダリングの状況を確認するには: 🎜🎜🎜🎜🎜🎜🎜 特定の配列要素 (pet[x].age) 内の値のみを変更すると、他の配列要素も再レンダリングされることがわかります。これは、Pet.props.addAge と Pet.props.reduceAge が親コンポーネント PetsView がレンダリングされるたびに再生成される匿名オブジェクトであるため、PureComponent はこれを使用してコンポーネントが依存するデータが更新されたと判断し、re をトリガーします。 -レンダリング。この問題は、React.Component とカスタム shouldComponentUpdate ライフサイクル関数を使用することで手動で解決できますが、親コンポーネント PetsView がレンダリングされるたびに匿名サブコンポーネント プロパティ値が再生成され、これによりコンピューティング リソースも消費されます。 🎜父组件向子组件传递 index 值:这种模式是父组件向子组件传递 index 值,并在子组件内部实现自身的事件处理逻辑,如下:
class PetsView extends PureComponent { ... render() { ... return ( <p style={{ padding: 10, margin: 10 }}> ... { state.map((pet, index) => <PetView state={pet} key={pet.id} index={index} // 直接把 index 值传递给子组件 />) } ... </p> ) } }
class PetView extends PureComponent { // 在子组件实现动作逻辑 // 调用时传递 index addAge(index){ state.pets[index].age += 1 } // 或函数自行从 props 获取 index reduceAge = () => { // 函数内部使用到 this 对象,使用 xxx = () => {...} 来定义组件属性更方便 state.pets[this.props.index].age -= 1 } render() { /** @type {initState['pets'][0]} */ let state = this.props.state; let index = this.props.index; return ( <p > <li> {state.name}: <button onClick={() => this.reduceAge(index)}> - </button> {/* 使用闭包传递 index 值 */} {state.age} <button onClick={this.addAge}> + </button> {/* 或让函数实现自己去获取index值 */} years old. </li> </p> ) } }
这种模式可以使子组件获取 index 并处理自身的动作逻辑,而且子组件也可以把自身所在的序号显示出来,具有较强的灵活性。我们再来看看其当元素内部 state 改变时,组件的重新渲染情况:
我们发现,数组元素组件可以很好地按需渲染,在渲染数组元素的情况下这种方法具有较高的运行效率。
但是,由于元素组件内部操作函数绑定了唯一位置的 state 操作逻辑,如addAge(index){ state.pets[index].age += 1}
。假设我们还有 state.children
数组,数组元素的格式与 state.pets
一样, 我们要用相同的元素组件来同时显示和操作这两个数组时,这种数组渲染模式就不适用了。我们可以用第1种方案实现这种情况的需求,但第1种方案在渲染效率上不是很完美。
Pastate 的 imState 的每个节点本身带有节点位置的信息和 store 归宿信息,我们可以利用这一点来操作数组元素!
我们使用 getResponsiveState 函数获取 imState 对于的响应式 state,如下:
class PetsView extends PureComponent { ... render() { ... return ( <p style={{ padding: 10, margin: 10 }}> ... { state.map((pet, index) => <PetView state={pet} key={pet.id} {/* 注意,这里无需传递 index 值,除非要在子组件中有其他用途*/} />) } ... </p> ) } }
import {..., getResponsiveState } from 'pastate' class PetView extends PureComponent { addAge = () => { /** @type {initState['pets'][0]} */ let pet = getResponsiveState(this.props.state); // 使用 getResponsiveState 获取响应式 state 节点 pet.age += 1 } reduceAge = () => { /** @type {initState['pets'][0]} */ let pet = getResponsiveState(this.props.state); // 使用 getResponsiveState 获取响应式 state 节点 pet.age -= 1 } render() { /** @type {initState['pets'][0]} */ let state = this.props.state; return ( <p > <li> {state.name}: <button onClick={this.reduceAge}> - </button> {state.age} <button onClick={this.addAge}> + </button> years old. </li> </p> ) } }
我们可以看到,子组件通过 getResponsiveState 获取到当前的 props.state 对应的响应式 state,从而可以直接对 state 进行复制修改,你无需知道 props.state 究竟在 store.state 的什么节点上! 这种模式使得复用组件可以在多个不同挂载位置的数组中使用,而且可以保证很好的渲染性能:
Pastate 提供个三个直接操作 imState 的函数,分别为 set
, merge
, update
。我们来演示用这些操作函数来代替 getResponsiveState
实现上面操作宠物年龄的功能:
import {..., set, merge, update } from 'pastate' class PetView extends PureComponent { addAge = () => { set(this.props.state.age, this.props.state.age + 1); } reduceAge = () => { merge(this.props.state, { age: this.props.state.age - 1 }); } reduceAge_1 = () => { update(this.props.state.age, a => a - 1); } ... }
可见,这种 imState 操作函数的模式也非常简单!
使用 pastate 数组元素操作方案的注意事项:当操作的 state 节点的值为 null 或 undefined 时, 只能使用 merge
函数把新值 merge 到父节点中,不可以使用 getResponsiveState
,set
或 update
。我们在设计 state 结构时,应尽量避免使用绝对空值,我们完全可以用 ''
, []
等代替绝对空值。
下一章,我们来看看如何在 pastate 中渲染和处理表单元素。
这是 Pastate.js 响应式 react state 管理框架系列教程的第三章,欢迎关注,持续更新。
这一章我们来看看在 pastate 中如何渲染和处理 state 中的数组。
相关推荐:
Pastate.js 之响应式 react state 管理框架
以上がPastate.js 応答フレームワークの配列レンダリングと操作の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。