实现事件响应

大家好,我是万维读客的讲师曹欢欢。前面章节我们实现了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

参考

  1. responding-to-events文档:https://zh-hans.react.dev/learn/responding-to-events


请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部