x 互动
此模块允许您写入响应事件并更改自身数据的 x- dash 组件 .
安装
此模块在节点16上得到支持,并在npm上得到分发.
npm install --save @financial-times/x-interaction
x-interaction 用于内部 x-dash 组件,而不是应用程序的依赖.
写入交互式组件
主接口 x-interaction 是函数 withActions,可以用来包裹普通(即. 无国籍人x-dash组件.
行动
动作是传递给组件的函数,意在用于事件处理器. 每个动作返回 a 状态更新,它既可以是拟合并到组件属性中的对象,也可以是与组件属性合并的函数 当前 属性,并返回要合并的对象:
import {withActions} from '@financial-times/x-interaction';
const greetingActions = withActions({
actionOne() {
return {greeting: "world"};
},
actionTwo() {
return ({greeting}) => ({
greeting: greeting.toUpperCase(),
});
},
});
const Greeting = greetingActions(({greeting, actions}) => <div>
hello {greeting}
<button onClick={actions.actionOne}>"world"</button>
<button onClick={actions.actionTwo}>uppercase</button>
</div>);
点击第一个按钮会将问候设置为 "world",然后单击第二个按钮,将问候设置到之前的大小写。 从您的动作中返回一个函数是使更新状态依赖于先前状态的唯一方法.
同步动作
如果一个动作返回一个承诺(例如如果它是一个) async 函数), x-interaction 将等待承诺 并使用它决定的值 当国家更新, 并设置属性 isLoading 改为 true 誓以飞翔的盟誓
import {withActions} from '@financial-times/x-interaction';
const greetingActions = withActions({
async updateGreeting() {
const repsonse = await fetch('/greeting');
const {greeting} = await response.json();
return {greeting};
},
});
const Greeting = greetingActions(({greeting, actions, isLoading}) => <div>
hello {greeting}
<button onClick={actions.updateGreeting} disabled={isLoading}>
{isLoading ? 'loading greeting...' : 'update greeting'}
</button>
</div>);
动作中的属性
如果您需要访问组件的属性 在此之前 返回状态更新,例如如果组件需要将参数发送到一个终点,可以将函数传递到 withActions 替换动作对象。 您的功能将随 初次 属性,并返回动作对象:
import {withActions} from '@financial-times/x-interaction';
const greetingActions = withActions(({lang}) => ({
async updateGreeting() {
const repsonse = await fetch(`/greeting?lang=${lang}`);
const {greeting} = await response.json();
return {greeting};
},
}));
const Greeting = greetingActions(({greeting, actions, isLoading}) => <div>
hello {greeting}
<button onClick={actions.updateGreeting} disabled={isLoading}>
{isLoading ? 'loading greeting...' : 'update greeting'}
</button>
</div>);
当状态更新时, 这些属性将不会改变,因此它们不应用于有依赖于先前状态的状态;为此使用状态更新函数.
已包装和未包装组件
由于动作与组件是分开的,因此基组件通常可以作为一个静态组件而不用包装. 如果该组件的消费者需要提供自己的行动,这样做是有用的。 建议将包裹组件,基组件,动作作为单独命名的出口输出. 其命名惯例是 ComponentName, (中文(简体) ), BaseComponentName 财务报告和审定财务报表 componentNameActions,例如:
import {withActions} from '@financial-times/x-interaction';
export const greetingActions = withActions({});
export const BaseGreeting = ({greeting, actions}) => <div>
hello {greeting}
</div>;
export const Greeting = greetingActions(BaseGreeting);
将服务器上架的标记加水
水分:客户端JavaScript将服务器端渲染完成的静态HTML网页转换为动态网页的技术,将事件处理器附加到HTML元素中.
当你有一个 x-interaction 组件由服务器提供,您想要附加组件的客户端版本来处理动作,而不是手动渲染组件(这可能会变得不易操作,特别是如果页面上有许多组件实例),您可以拥有 x-interaction 为您管理它.
这有三个部分:记录组件、序列化和水分化.
登记该构成部分
要注册您需要拨打的组件 x-interaction是因为 registerComponent 函数,将组件及其名称作为参数.
import {withActions, registerComponent} from '@financial-times/x-interaction';
const greetingActions = withActions({
actionOne() {
return {greeting: "world"};
},
actionTwo() {
return ({greeting}) => ({
greeting: greeting.toUpperCase(),
});
},
});
const Greeting = greetingActions(({greeting, actions}) => <div>
hello {greeting}
<button onClick={actions.actionOne}>"world"</button>
<button onClick={actions.actionTwo}>uppercase</button>
</div>);
registerComponent(Greeting, 'Greeting')
序列
为确保在客户端以相同的初始数据提供组件,并跟踪提供的所有实例及其识别资料, x-interaction 出口a Serialiser 课。 你个 必须 每个 HTTP 请求在启动时创建新实例,以响应任意数量 x-interaction 组件.
这个案例应该传给每一个 x-interaction 作为名为 serialiser。这将在发送给客户端的数据("水分数据")中添加组件的属性.
终于,每一次 x-interaction 组件,您应当输出水分数据. x-interaction 出口a HydrationData 组件,将序列器作为财产并制作 <script> 包含水分数据的标签,指定给一个全局变量,该变量可由 x-interaction 客户端运行时间 。 序列器数据输出后无法再次使用 HydrationData 组件.
这是使用 Serialiser 财务报告和审定财务报表 HydrationData 使用 Greeting 我们在前一个步骤中注册的组件.
import express from 'express';
import { Greeting } from './Greeting'
import { Serialiser, HydrationData } from '@financial-times/x-interaction';
const app = express();
app.get('/', (req, res) => {
const serialiser = new Serialiser();
res.send(`
${Greeting({ serialiser })}
${HydrationData({ serialiser })}
`);
});
水分处理
当在服务器侧面渲染时,组件输出一个额外的包裹元素,并带有 data-x-dash-id 属性。 此为 x-interaction 运行时间以识别序列化数据中的组件,并将正确的实例附加在客户端. 你可以把这个作为 id 属性;否则将随机生成标识符.
x-interaction 导出一个函数 hydrate。这应当呼吁客户方。 它检查页面上的全局序列化数据,使用标识符来查找包装元素,并呼叫 render 从你选择的 x-engine 客户端运行时间,将组件实例放入包装中.
打电话之前 hydrate,必须首先 import 任意 x-interaction 在页面上显示的组件。 组件在 x-interaction 导入时运行时间; 您不需要对导入的组件做任何事情 。 这将确保组件包含在您的客户端捆绑中 。 同样,如果你是服务器侧渲染的组件只是你通过 withActions,确保您在输入该组件的同时输入其寄存器.
因为 hydrate 期望包装纸在 DOM 中被调用时, 它应该在后面调用 DOMContentLoaded。取决于您的页面结构,在滚动到视图时,将组件水合可能是合适的.
水合组件客户端代码的完整示例 :
import { hydrate } from '@financial-times/x-interaction';
import '@financial-times/x-increment'; // bundle x-increment and register it with x-interaction
document.addEventListener('DOMContentLoaded', () => hydrate());
如果基础组件需要不能被序列化的属性,例如函数或其他组件,你可以将这些属性传递给 hydrate,只要它们能成为该部分每个实例的同值. 论点 hydrate 是对象映射 x-interaction含有额外属性的对象的组件的内部名称,以传递给该组件的每个位置。 您可以通过调用访问一个组件的内部名称 getComponentName。 。 。 .
比如说, x-interaction 支持 a customSlot 在按钮中渲染反应元素的属性,但不能序列化。 为了让客户知道这一点 我们可以把它作为额外的水合物
import { hydrate, getComponentName } from '@financial-times/x-interaction';
import Increment from '@financial-times/x-increment';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
document.addEventListener('DOMContentLoaded', () => {
hydrate({
[getComponentName(Increment)]: {
customSlot: <FontAwesomeIcon icon='plus' />
}
})
});
外部触发行动
客户端渲染
当使用客户端运行时间( 如 React) 来渲染 x-interaction 组件,您可以通过属性 actionsRef 到组件。 这应当是一种功能,在使用该组件时,将参照组件实例的行动 null 当取出. 这和React类似 ref 属性 用于获取 DOM 元素的引用.
你可以从应用程序的任何地方调用这些动作引用, 并且它们会被放大到从你得到的引用的组件实例。 这允许外部事件,比如来自不是以x-dash为单位的组件,或者您不控制的第三方组件,触发您组件的状态变化. 也可以使用此功能来触发组件的初始化逻辑,例如当其被滚动到视图时.
这里有一个例子 触发一个 x-increment 组件的 increment 从组件外的按钮中操作 :
import { h, Component } from '@financial-times/x-engine';
import { Increment } from '@financial-times/x-increment';
class ExternalActionDemo extends Component {
render() {
return <div>
<button onClick={() => this.incrementActions && this.incrementActions.increment()}>
Increment externally
</button>
<Increment count={1} actionsRef={actions => this.incrementActions = actions} />
</div>;
}
}
服务器侧渲染
当您的组件由 x-interaction 客户端水合,无法访问组件实例,所以不能使用 actionsRef在此情况下, x-interaction 支持通过 a 启动动作 DOM 自定义事件。 。 。 .
这个活动叫做 x-interaction.trigger-action,并应当在您想要触发动作的组件实例的包装元件上发出。 包装有属性 data-x-dash-id,可以在选择器中使用,但由于随机生成的默认值 id 无法预测,你应该使组件 一个独特的,静态的 id。在服务器侧:
res.send(`
${Increment({count: 1, id: 'x-interaction-1'})}
${getInteractionData()}
`);
而客户端:
const wrapper = document.querySelector('[data-x-dash-id="x-interaction-1"]');
在那个 detail 事件参数,包括属性 action,这是触发动作的名称。 您也可以指定属性 args,它应该是一个阵列,它将被传递给行动作为其论据:
wrapper.dispatchEvent(
new CustomEvent(
'x-interaction.trigger-action',
{ detail: { action: 'increment', args: [ { amount: 5 } ] } }
)
);