类型错误:使用 React.Js 时无法将属性“innerHTML”设置为 null

时间:2021-02-27 05:54:37

标签: javascript reactjs

我是 React.js 的新手,我正在使用 React.js 和 Bootstrap 创建一个 Notes takign 网站。为简单起见,我将笔记数据保存在本地存储中,但我将升级此应用以将笔记数据保存在 Firebase 中。但是,当我尝试从本地存储加载笔记并将它们显示在 id 'notes' 的 div 元素中时,它给出了以下错误:TypeError: Cannot set property 'innerHTML' of null

我想在加载所有 html 或其他所有内容后运行加载注释功能。 当我在所有 HTML 加载后使用按钮单击侦听器运行此函数时,它可以工作。

代码是:

import React from 'react';

import './Home.css';

function Home() {
    const loadNotes = () => {
        let notes = localStorage.getItem('notes');
        let titles = localStorage.getItem('titles');

        let notesObj;
        let titlesObj;

        if (notes == null) {
            notesObj = [];
        }
        else {
            notesObj = JSON.parse(notes);
        }

        if (titles == null) {
            titlesObj = [];
        }
        else {
            titlesObj = JSON.parse(titles);
        }

        let html = '';
        notesObj.forEach(function (element, index) {
            html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
                        <div class="card-body">
                            <h5 class="card-title">Note</h5>
                            <p class="card-text">${element}</p>
                            <button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
                        </div>
                    </div>`
        });

        titlesObj.forEach(function (er, i) {
            html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
        });
    
        let notesElm = document.getElementById('notes');

        if (notesObj.length != 0) {
            notesElm.innerHTML = html;
        }

        else {
            notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
        }

        console.log('Notes shown.')
    }

    const addNote = () => {
        let addTxt = document.getElementById('addTxt');
        let notes = localStorage.getItem('notes');
        
        let addTitle = document.getElementById('addTitle');
        let titles = localStorage.getItem('titles');

        let notesObj;
        let titlesObj;

        if (notes == null) {
            notesObj = [];
        }
        else {
            notesObj = JSON.parse(notes);
        }

        if (titles == null) {
            titlesObj = [];
        }
        else {
            titlesObj = JSON.parse(titles);
        }

        notesObj.push(addTxt.value);
        titlesObj.push(addTitle.value);

        localStorage.setItem('notes', JSON.stringify(notesObj));
        localStorage.setItem('titles', JSON.stringify(titlesObj));

        addTxt.value = '';
        addTitle.value = '';

        loadNotes();
        
        console.log("Note added.")
    }
    return (
        <div className="home">
            <style type="text/css">
                {`
                    .btn {
                        margin-right: 10px;t
                    }

                    .home__mainTitle {
                        margin-top: 60px;
                    }
                `}
            </style>

            <div class="container my-3">
                <h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
                <div class="card">
                    <div class="card-body" id = 'editor'>
                        <h5 class="card-title">Add title</h5>
                        <div class="form-group">
                            <input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
                        </div>
                        <h5 class="card-title">Add notes</h5>
                        <div class="form-group">
                            <textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
                        </div>
                        <button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
                        <button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
                    </div>
                </div>
                <h1 className='home__notesTitle'>Your Notes</h1>
                <hr/>
                <div id="notes" class="row container-fluid">

                    {/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
                        <div class="card-body">
                            <h5 class="card-title">Note 1</h5>
                            <p class="card-text"></p>
                            <a href="#" class="btn btn-primary">Delete Note</a>
                        </div>
                    </div> --> */}

                    { loadNotes() }

                </div>
            </div>
        </div>
    );
}

export default Home;

对代码或问题描述中的任何错误表示感谢和抱歉。

2 个答案:

答案 0 :(得分:1)

通过尝试访问 DOM 并直接设置 innerHTML,您有点与 React 的一些一般原则作斗争。

在这种特定情况下,它失败了,因为当您第一次尝试对其进行变异时,DOM 中实际上并不存在 div。

看看这个非常局部的重构:

import React, { useEffect, useState } from 'react';
import './Home.css';

function Home() {
    const [notes, setNotes] = useState([]);
    const [titles, setTitles] = useState([]);

    useEffect(() => {
      setNotes(JSON.parse(localStorage.getItem('notes')) ?? []);
      setTitles(JSON.parse(localStorage.getItem('titles')) ?? []);
    },[]);

    const addNote = () => {
        let addTxt = document.getElementById('addTxt');
        
        let addTitle = document.getElementById('addTitle');

        let newTitles = [...titles, addTitle.value];
        let newNotes = [...notes, addTxt.value];

        localStorage.setItem('notes', JSON.stringify(newNotes));
        localStorage.setItem('titles', JSON.stringify(newTitles));

        setNotes(newNotes)
        setTitles(newTitles)

        
        addTxt.value = '';
        addTitle.value = '';

        console.log("Note added.")
    }
    return (
        <div className="home">
            <style type="text/css">
                {`
                    .btn {
                        margin-right: 10px;t
                    }

                    .home__mainTitle {
                        margin-top: 60px;
                    }
                `}
            </style>

            <div class="container my-3">
                <h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
                <div class="card">
                    <div class="card-body" id = 'editor'>
                        <h5 class="card-title">Add title</h5>
                        <div class="form-group">
                            <input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
                        </div>
                        <h5 class="card-title">Add notes</h5>
                        <div class="form-group">
                            <textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
                        </div>
                        <button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
                        <button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
                    </div>
                </div>
                <h1 className='home__notesTitle'>Your Notes</h1>
                <hr/>
                <div id="notes" class="row container-fluid">

                    {notes.map((note,index) => {

                    return <div class="noteCard my-2 mx-2 card" style={{width: '18rem'}} id={index}>
                        <div class="card-body">
                            <h5 class="card-title">{titles[index]}</h5>
                            <p class="card-text">{note}</p>
                            <button id={index} onclick='deleteNote(this.id)' 
                               class="btn btn-primary"><i class="fa fa-trash" style={{marginRight: "10px;"}}></i>Delete Note</button>
                        </div>
                    </div>

                    })}

                    {notes.length === 0 ? <h4>Nothing to show here.</h4> : null}

                </div>
            </div>
        </div>
    );
}

export default Home;

注意我是如何使用 useState 来存储笔记和标题的。文档:https://reactjs.org/docs/hooks-state.html

useEffect 在组件挂载并从 localStorage 加载数据时调用。 https://reactjs.org/docs/hooks-effect.html

然后,在渲染的主体中,我没有调用 loadNotes 并尝试改变尚不存在的 DOM,而是map notes 和 {{1 }} 进入渲染的内容。

请注意,这还不是完整的重构。例如,您可能希望向文本区域添加侦听器以自动跟踪内容,而不是使用 titles 拉取内容。此外,document.getElementById 尚未实现,useEffect 中对 localStorage 内容的测试非常少,等等。但是,这足以让您入门。

答案 1 :(得分:0)

发生此错误是因为 loadNotes() 被包裹在音符 div 中。因此,您可以检查是否首先使用 notes 条件创建了 if div。

if (notesElm) {
  if (notesObj.length != 0) {
    notesElm.innerHTML = html;
  }

  else {
      notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
  }
}

以下是工作代码:

import React from 'react';

import './Home.css';

function Home() {
    const loadNotes = () => {
        let notes = localStorage.getItem('notes');
        let titles = localStorage.getItem('titles');

        let notesObj;
        let titlesObj;

        if (notes == null) {
            notesObj = [];
        }
        else {
            notesObj = JSON.parse(notes);
        }

        if (titles == null) {
            titlesObj = [];
        }
        else {
            titlesObj = JSON.parse(titles);
        }

        let html = '';
        notesObj.forEach(function (element, index) {
            html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
                        <div class="card-body">
                            <h5 class="card-title">Note</h5>
                            <p class="card-text">${element}</p>
                            <button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
                        </div>
                    </div>`
        });

        titlesObj.forEach(function (er, i) {
            html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
        });
    
        let notesElm = document.getElementById('notes');
        if (notesElm) {
          if (notesObj.length != 0) {
            notesElm.innerHTML = html;
          }

          else {
              notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
          }
        }
       

        console.log('Notes shown.')
    }

    const addNote = () => {
        let addTxt = document.getElementById('addTxt');
        let notes = localStorage.getItem('notes');
        
        let addTitle = document.getElementById('addTitle');
        let titles = localStorage.getItem('titles');

        let notesObj;
        let titlesObj;

        if (notes == null) {
            notesObj = [];
        }
        else {
            notesObj = JSON.parse(notes);
        }

        if (titles == null) {
            titlesObj = [];
        }
        else {
            titlesObj = JSON.parse(titles);
        }

        notesObj.push(addTxt.value);
        titlesObj.push(addTitle.value);

        localStorage.setItem('notes', JSON.stringify(notesObj));
        localStorage.setItem('titles', JSON.stringify(titlesObj));

        addTxt.value = '';
        addTitle.value = '';

        loadNotes();
        
        console.log("Note added.")
    }
    return (
        <div className="home">
            <style type="text/css">
                {`
                    .btn {
                        margin-right: 10px;t
                    }

                    .home__mainTitle {
                        margin-top: 60px;
                    }
                `}
            </style>

            <div class="container my-3">
                <h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
                <div class="card">
                    <div class="card-body" id = 'editor'>
                        <h5 class="card-title">Add title</h5>
                        <div class="form-group">
                            <input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
                        </div>
                        <h5 class="card-title">Add notes</h5>
                        <div class="form-group">
                            <textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
                        </div>
                        <button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
                        <button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
                    </div>
                </div>
                <h1 className='home__notesTitle'>Your Notes</h1>
                <hr/>
                <div id="notes" class="row container-fluid">

                    {/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
                        <div class="card-body">
                            <h5 class="card-title">Note 1</h5>
                            <p class="card-text"></p>
                            <a href="#" class="btn btn-primary">Delete Note</a>
                        </div>
                    </div> --> */}

                    { loadNotes() }

                </div>
            </div>
        </div>
    );
}

export default Home;