我正在使用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);
答案 0 :(得分:2)
作为一般规则,将ReactJS与非ReactJS处理方式混合起来是一个坏主意。 React有一种非常具体的处理事件的方式,并且直接调用addEventListener()
并不好玩。当你致电addEventListener()
它有时 时,很有可能,但你真的不应该依赖它。
你不应该混淆两者的原因是React可以随时重建DOM 任意时间:任何时候状态发生变化,React都可以拆除DOM元素和新元素,由它自行决定,不是你的。你们既不知道也不关心元素的渲染时间,以及React之美的一部分。
那么你如何处理事件呢?在创建元素时,您必须使用onChange
和onKeyUp
之类的属性将它们连接起来。 (在引擎盖下,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添加自定义钩子。
这里是创建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;