이 글에서 공유한 내용은 Pastate.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> ) } }
여기에는 두 개의 구성 요소가 정의되어 있습니다. 첫 번째는 애완 동물 배열을 표시하는 데 사용되는 PetsView이고, 두 번째는 애완 동물 요소를 표시하는 데 사용되는 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> ) } } ...
완료! 네이티브 리액트를 사용한 배열 렌더링 패턴과 동일한 배열 객체 렌더링에 성공했습니다. 페이지 결과는 다음과 같습니다.
먼저 배열 요소를 추가하거나 빼겠습니다. 이는 pasate Simple을 사용하여 구현하기가 매우 쉽습니다. vue.js에서 영감을 받아 Pastate는 store.state 배열 노드의 다음 7가지 배열 변형 방법을 향상했습니다. 이러한 배열 함수를 직접 호출할 수 있으며 Pastate는 자동으로 뷰 업데이트를 트리거합니다. 이 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()
🎜🎜push와 pop을 사용하여 배열을 업데이트해 보겠습니다. 🎜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가 배열 요소를 표시하는 데 사용된다는 것을 알 수 있습니다. 여러 번 사용되었습니다. 우리는 이러한 유형의 구성 요소를 여러 위치에서 🎜사용되는🎜 🎜다중 인스턴스 구성 요소🎜라고 부릅니다. 다중 인스턴스 구성 요소의 내부 작업 처리 논리는 구성 요소 인스턴스의 특정 위치에 따라 결정되는데, 이는 단일 인스턴스 구성 요소의 처리 모드와 다릅니다. 🎜🎜우리는 애완동물의 나이를 조정하기 위해 각 애완동물 보기에 두 개의 버튼을 추가하는 방법을 만들려고 합니다. 우리는 두 가지 전통적인 솔루션과 파스타 솔루션을 각각 사용하여 구현합니다: 🎜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는 이를 사용하여 구성 요소가 의존하는 데이터가 업데이트되었다고 생각하므로 다시 트리거됩니다. -표현. 이 문제는 사용자 정의 shouldComponentUpdate 수명 주기 함수와 함께 React.Component를 사용하여 수동으로 해결할 수 있지만 익명의 하위 구성 요소 속성 값은 상위 구성 요소 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!