React中的addEventListener为null

时间:2017-03-01 12:43:52

标签: javascript reactjs

我正在使用React来创建搜索功能。我之前使用过这种方式,但没有使用React,它已经有效了。现在它在控制台中出现一个错误,说“" Uncaught TypeError:无法读取属性' addEventListener' of null"。这可能与它的反应有关吗?我该如何解决这个问题?

<body>
<div id="searching"></div>
<script type="text/babel">
    class SearchBox extends React.Component { render(){ return(
    <form>
        <input type="text" className="searchbox" />
    </form>) } } ReactDOM.render(
    <SearchBox />, document.getElementById("searching"));
</script>

JS

const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
const repositories = [];

fetch(endpoint)
.then(blob => blob.json())
.then(data => repositories.push(...data));

function findMatches(wordToMatch, repositories) {
return repositories.filter(place => {

    const regex = new RegExp(wordToMatch, "gi");
    return place.city.match(regex) || place.state.match(regex);
    });
};

function displayMatches() {
const matchArray = findMatches(this.value, repositories);
const html = matchArray.map(place => {
    const regex = new RegExp(this.value, "gi");
    const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
    const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
    return `
  <li>
  <span class="name">${cityName}, ${stateName}</span>
  <span class="population">${numberWithCommas(place.population)}</span>
  </li>
  `;
}).join('');
console.log(matchArray);
suggestions.innerHTML = html;
};

const searchInput = document.querySelector(".searchbox");
const suggestions = document.querySelector(".suggestions");

searchInput.addEventListener("change", displayMatches);
searchInput.addEventListener("keyup", displayMatches);

4 个答案:

答案 0 :(得分:2)

作为一般规则,将ReactJS与非ReactJS处理方式混合起来是一个坏主意。 React有一种非常具体的处理事件的方式,并且直接调用addEventListener()并不好玩。当你致电addEventListener()它有时 时,很有可能,但你真的不应该依赖它。

你不应该混淆两者的原因是React可以随时重建DOM 任意时间:任何时候状态发生变化,React都可以拆除DOM元素和新元素,由它自行决定,不是你的。你们既不知道也不关心元素的渲染时间,以及React之美的一部分。

那么你如何处理事件呢?在创建元素时,您必须使用onChangeonKeyUp之类的属性将它们连接起来。 (在引擎盖下,React实际上会在正确的时间使用addEventListener,但你从未真正注意到这种情况。)

然后,修改后的代码版本的结构可能是这样的:

class SearchBox extends React.Component {

    constructor(props) {
        super(props);

        // This binding is necessary to make `this` work in the callback
        this.onKeyUp = this.onKeyUp.bind(this);
        this.onChange = this.onChange.bind(this);
    }

    onChange(event) {
        ...
    }

    onKeyUp(event) {
        ...
    }

    render() {
        return(
            <form>
                <input type="text" className="searchbox" onChange={this.onChange} onKeyUp={this.onKeyUp} />
            </form>
        );
    }
}

ReactDOM.render(
    <SearchBox />,
    document.getElementById("searching")
);

正如您所看到的,代码的整个结构是不同的。 React不仅仅是一种轻松发布HTML元素的方法:它是一个完整的基于组件的微框架,您可以围绕它设计整个UI。

答案 1 :(得分:2)

我同意@Sean。原生Java和ReactJS具有不同的生命周期来处理事件。 我添加了一些使用钩子的人。您可以使用useEffect和useRef添加自定义钩子。

  1. 将事件处理程序创建为Ref,并在使用初始化时绑定它 useEffect
  2. 在useEffect中将事件与目标元素和侦听器绑定。

这里是创建useEventListener自定义钩子的示例代码。 我从https://usehooks.com/useEventListener/

获得了此示例代码
function useEventListener(eventName, handler, element = window){
  // Create a ref that stores handler
  const savedHandler = useRef();

  // Update ref.current value if handler changes.
  // This allows our effect below to always get latest handler ...
  // ... without us needing to pass it in effect deps array ...
  // ... and potentially cause effect to re-run every render.
  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(
    () => {
      // Make sure element supports addEventListener
      // On 
      const isSupported = element && element.addEventListener;
      if (!isSupported) return;

      // Create event listener that calls handler function stored in ref
      const eventListener = event => savedHandler.current(event);

      // Add event listener
      element.addEventListener(eventName, eventListener);

      // Remove event listener on cleanup
      return () => {
        element.removeEventListener(eventName, eventListener);
      };
    },
    [eventName, element] // Re-run if eventName or element changes
  );
};
export default useEventListener;

答案 2 :(得分:1)

我不同意肖恩。使用本机函数很好,实际上你有时也必须这样做。

您收到此错误的原因是您的脚本正在尝试将侦听器添加到不存在的DOM节点:当您的侦听器脚本执行时,您的输入元素尚未刷新到DOM 。这就是当你需要以这种方式达到逃生舱时,你应该在组件本身内,在compnentDidMount中,并且只有当一个孩子需要监听一个完全未连接的父DOM节点时才这样做 - 一个用于例如,可能在其他React子树之外的yiur组件属于。这可以保证您尝试查找的节点已存在。

话虽这么说,使用本机功能这样一种逃避舱口,应该并且可以在95%的常见用例中避免。你应该使用更多的惯用方法,通过将状态提升到更高级别的组件,或者如果你真的需要使用引用。

答案 3 :(得分:0)

取决于以上答案和其他地方的参考。我尝试了以下方法并解决了问题。

class Searchbox extends React.Component {
    state = {};

    componentDidMount = () => {  //Used for when component mounts for first time
       myfunction();
    }

    componentDidUpdate = () => {  //Used for every time component updates
       myfunction();
    }

    myfunction = () => {
       // This is function which contains addEventListeners
       // or statements like - document.getElementId("myDiv");
    }

    render() {
       return (
           <div id="myDiv">
               ...
               // body of code to render
           </div>
       );
    }
}

export default Searchbox;