「这是我参与2022首次更文挑战的第39天,活动详情查看:2022首次更文挑战」。
写在前面
- 前面一篇文章,我们已经实现,在 Form 组件中获取数据仓库的操作权限对象,然后通过 Context 将其传递给子孙组件,最后在 Field 组件中接收到数据仓库的操作权限,并接管 Field 组件的 children 的 onChange 事件,完成 Form 组件中所有数据统一管理的需求
- 但是前面只是实现了,Field 组件 children 与数据仓库的交互,数据仓库数据更新后,相应的组件该如何进行更新渲染呢?
- 下面我们就来解决这个问题
Field 组件响应数据更新
- 为了解决组件响应数据更新的问题,我的大体思路就是:
- 在每个 Field 组件上,定义一个组件更新渲染的方法
- 在数据仓库中注册活跃的 Field 组件实例
- 然后数据仓库的数据发生更新后,立即调用 Field 组件实例的更新方法,更新组件渲染
- 接下来我们来实现它
export class Field extends Component {
static contextType = FormContext;
// 将接收的组件变为受控组件
getControlled = () => {
const { name } = this.props;
const { setFieldValue, getFieldValue } = this.context;
return {
value: getFieldValue(name),
onChange(e) {
const newVal = e.target.value;
setFieldValue(name, newVal);
},
};
};
// 数据改变时,更新组件的方法
onStoreChange = () => {
this.forceUpdate();
};
componentDidMount() {
// 在 store 中注册 field 组件的 实例
this.unRegister = this.context.registerField(this);
}
// 注销 store 中的组件实例
componentWillUnmount() {
this.unRegister();
}
render() {
const controlledChild = React.cloneElement(
this.props.children,
this.getControlled()
);
return controlledChild;
}
}
- 可以看到,上面增加了几个方法
- onStoreChange,就是我们的 Field 组件更新渲染方法
- context.registerField,就是我们注册 Field 组件实例的方法,下面会实现它
- unRegister,就是我们注销不活跃 Field 组件实例的方法,它是 context.registerField 方法返回的结果函数
完善数据仓库的操作权限
- 回到数据仓库的代码中,实现上面提到的 registerField 方法
import { useRef } from "react";
// 1、实现一个专属 Form 的状态管理库
class FormStore {
constructor() {
this.store = {}; // 用于存放组件数据
this.fieldEntities = []; // 用于存放 field 组件实例
}
// 2、实现操作状态的方法 get set
// 2.1、多个字段的 set 操作
setFieldsValue = (newStore) => {
// 先更新 store 中的数据
this.store = {
...this.store,
...newStore,
};
// 然后更新组件
this.fieldEntities.forEach((instance) => {
Object.keys(newStore).forEach((name) => {
// 只有当 newStore 中的相关 name 的组件的状态发生改变时,才更新对应的组件
if (name === instance.props.name) {
instance.onStoreChange();
}
});
});
};
// 2.2、多个字段的 get 操作
getFieldsValue = () => ({ ...this.store });
// 2.3、单个字段的 set 操作
setFieldValue = (field, val) => {
this.setFieldsValue({ [field]: val });
};
// 2.4、单个字段的 get 操作
getFieldValue = (fieldName) => this.store[fieldName];
// 3、注册需要更新的组件实例
registerField = (fieldInstance) => {
this.fieldEntities.push(fieldInstance);
// 返回一个函数,用于注销已注册的组件实例,并清空 store 中的相关数据
return () => {
this.fieldEntities = this.fieldEntities.filter(
(instance) => instance !== fieldInstance
);
delete this.store[fieldInstance.props.name];
};
};
// 暴露操作状态的方法
getForm = () => ({
getFieldsValue: this.getFieldsValue,
setFieldsValue: this.setFieldsValue,
setFieldValue: this.setFieldValue,
getFieldValue: this.getFieldValue,
registerField: this.registerField,
});
}
- 既然要注册 Field 组件实例,那么我们就需要初始化一个 fieldEntities 用于存放实例
- 在上面的代码中 registerField 方法
- 接收一个 Field 组件实例,然后直接 push 到 fieldEntities 中完成注册
- 然后返回注销 Field 组件实例的方法
- 有了 Field 组件实例,那么响应数据更新就好办了
- 在 setFieldsValue 中,循环组件实例,只要是组件实例的 name 属性与传入数据的 name 相同的,该组件都需要进行更新,直接调用实例的 onStoreChange 方法即可
小结
- 至此,我们呢就实现了,数据更新,Field 组件随即响应更新的需求了
- 后面的文章,我们将继续晚上,Form 注册回掉事件的功能,如:数据校验成功,数据校验失败回掉等
最后
- 今天的分享就到这里了,欢迎大家在评论区里面进行讨论 👏。
- 如果觉得文章写的不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力 🥰