实现事件响应
大家好,我是万维读客的讲师曹欢欢。前面章节我们实现了react渲染组件,也可以处理状态,但是还没有处理事件,本节我们看如何实现事件响应。
事件响应
关于事件响应,其实就是可以给DOM节点绑定事件,比如用户点击事件,处理用户点击的交互动作。可以参考官方文档:https://zh-hans.react.dev/learn/responding-to-events
这里给个简单的例子:
export default function Button() {
function handleClick() {
alert('你点击了我!');
}
return (
<button onClick={handleClick}>
点我
</button>
);
}
简单分析一下,这里是在节点上增加了一个onClick属性,属性内容是绑定的事件。我们前面处理fiber节点时候处理过属性,只要把事件属性拿出来单独处理即可。
编写测试用例
首先我们编写测试用例,新建文件夹treact06,复制上一节的代码和测试用例,因为我们是持续集成开发,所以后面每次改动都要保留之前的测试用例,需要保证之前的测试用例都能跑通过。
增加测试用例如下:
describe('event handler test', () => {
it('add event', async () => {
const globalObj = {};
function App() {
const [count, setCount] = Treact.useState(0);
globalObj.count = count;
return (
<div className="button" onClick={()=>{setCount(count+1)}}>{count}</div>
);
}
const container = document.createElement('div');
const root = Treact.createRoot(container);
await Treact.act(() => {
root.render(<App />);
expect(container.innerHTML).toBe('');
});
await Treact.act(() => {
container.querySelector('.button').click();
});
console.log('globalObj.count', globalObj.count);
expect(globalObj.count).toBe(1);
});
})
上面测试用例我们加入了onClick事件,然后更新状态数据。然后我们看控制台应该会报错的:
FAIL treact06/jsx.test.jsx > event handler test > add event
AssertionError: expected +0 to be 1 // Object.is equality
- Expected
+ Received
- 1
+ 0
❯ treact06/jsx.test.jsx:120:29
118| });
119| console.log('globalObj.count', globalObj.count);
120| expect(globalObj.count).toBe(1);
| ^
121| });
122| })
报错是正常的,下面我们来补充事件的处理。
实现事件
了解上面的原理之后,代码比较简单,大家可以自己写一下试试,只要测试用例能通过即可。
我们先处理之前过滤属性的地方,增加对事件属性的过滤,然后在处理事件属性,绑定对应的事件到fiber的stateNode上。代码如下:
const isEvent = (key) => /^on/.test(key);
const filerProps = (key) => !isEvent(key) && key != 'children';
...
if (!fiber.stateNode) {
fiber.stateNode = fiber.type === 'HostText' ? document.createTextNode('') : document.createElement(fiber.type);
Object.keys(fiber.props).filter(filerProps).forEach(key => {
fiber.stateNode[key] = fiber.props[key];
})
Object.keys(fiber.props).filter(isEvent).forEach(key => {
const eventName = key.toLowerCase().substring(2);
fiber.stateNode.addEventListener(eventName, fiber.props[key]);
})
}
保存后,查看测试用例结果,执行通过。
✓ treact06/jsx.test.jsx (4)
✓ async render Function Component (1)
✓ render with act
✓ fiber useState test (1)
✓ render useState
✓ fiber useReducer test (1)
✓ render useReducer
✓ event handler test (1)
✓ add event
Test Files 1 passed (1)
Tests 4 passed (4)
Start at 10:16:55
Duration 51ms
参考
- responding-to-events文档:https://zh-hans.react.dev/learn/responding-to-events