2

I've already asked that question but my explanation was pretty bad, so I decided to ask again with a better explanation and with actual code (I'll ask moderators to delete one of the posts). So let's consider the problem.

Following snippet represents rendering notes from array. However, during the adding note part, I mutate a state. So the question is: how can I add a new note in notes array without mutating? In other words, I want to remove replaceNotes and remain the same functionality. I know that it's possible to add notes without array at all, but I do need to update array with notes in due to the future reference. The ting is, in my original application I've got lists with notes, and while I switch between lists, I should get rendered notes that relies to the list I switch on. That's why I should keep the reference to notes array.

At the same time I'm wondering, would it be okay, if I just store notes in localStorage and then take notes from that data? Is it a good practice in functional programming?

const button = document.getElementById('button');
const notesContainer = document.querySelector('.notes');

const pipe = (f, g) => (...args) => f(g(...args));

let notes = [];

const createNote = (...fns) => fns.reduceRight(pipe);

const handleEvent = () =>
   createNote(gatherContent, renderContent, replaceNotes)(notes);

function gatherContent(notes) {
   const name = prompt('How do you want to name a note?');

   return [...notes, { name }];
}

function renderContent(notes) {
   function render(note) {
      const noteEl = document.createElement('div');
      noteEl.innerHTML = `<p>${note.name}</p>`;
      notesContainer.append(noteEl);
   }

   notesContainer.innerHTML = '';
   notes.map(render);

   return notes;
}

const replaceNotes = newNotes => (notes = newNotes);

button.addEventListener('click', handleEvent);
<button id="button">Click me!</button>
<section class="notes"></section>
h1kiga
  • 84
  • 9
  • 2
    I _think_ that your definition of `pipe` is known as `compose` in the functional programming world. – customcommander Jun 21 '20 at 18:15
  • I mean It'll be composition if I changed the order of functions as arguments in `createNote` and replaced `reduceRight` with `reduce`, won't it? – h1kiga Jun 21 '20 at 18:19
  • 2
    My understanding is that it is common that `pipe(f, g)(x)` = `g(f(x))` whereas `compose(f, g)(x)` = `f(g(x))`. – customcommander Jun 21 '20 at 18:23
  • That's true. I wanted to write like that at first but then something clicked in my brain and I wrote `reduceRight` lmao – h1kiga Jun 21 '20 at 18:31
  • You should be able to delete your own posts? – Jared Smith Jun 22 '20 at 14:17
  • I couldn't because a user proposed the answer for my problem. "Users have spent their time, helping to solve the problem and It'd be not fair to delete their answers" – h1kiga Jun 23 '20 at 04:25

1 Answers1

3

Here is how to create a simple task list app without mutating anything except for the DOM.

const button = document.getElementById("button");
const section = document.getElementById("notes");
const template = document.getElementById("template");

template.parentNode.removeChild(template);

const render = notes => {
    button.onclick = event => {
        const name = prompt("How do you want to name a note?");
        render([...notes, { name }]);
    };

    while (section.lastChild) {
        section.removeChild(section.lastChild);
    }

    for (const note of notes) {
        const node = template.cloneNode(true);
        node.firstChild.firstChild.nodeValue = note.name;
        section.appendChild(node);
    }
};

render([]);
<button id="button">Click me!</button>
<section id="notes"></section>
<div id="template"><p>name</p></div>

For a detailed explanation, read my previous answer. https://stackoverflow.com/a/58642199/783743


You can use this pattern with localStorage too.

const button = document.getElementById("button");
const section = document.getElementById("notes");
const template = document.getElementById("template");

template.parentNode.removeChild(template);

const render = notes => {
    localStorage.setItem("notes", notes); // set notes

    button.onclick = event => {
        const name = prompt("How do you want to name a note?");
        render([...notes, { name }]);
    };

    while (section.lastChild) {
        section.removeChild(section.lastChild);
    }

    for (const note of notes) {
        const node = template.cloneNode(true);
        node.firstChild.firstChild.nodeValue = note.name;
        section.appendChild(node);
    }
};

render(localStorage.getItem("notes") || []); // get notes

Note that localStorage should only be used to save state that you want to use across sessions. It's not recommended to use localStorage as your application store. That would result in both bad performance and bad code structure.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • That's an interesting of way of solving a problem like that and I'll definitely use it in some places. But I'm still wondering is using `localStorage` a good practice in functional programming? It seems as the best choice in my case – h1kiga Jun 22 '20 at 11:10
  • Do not use `localStorage` as your application store. I edited my answer to explain why. – Aadit M Shah Jun 22 '20 at 12:58
  • Thank you! Your explanation was very helpful. – h1kiga Jun 22 '20 at 14:02