How can I immutable update an array and store it in local storage in React?

I’m making a recipe app. I want useres to be able to add recipes to a list of favorites. There are 3 React components involved. Recipe, AddToFavorites, and Favorites.

The Recipe component shows various details about the selected recipe.

The AddToFavorites component is a button that is rendered in the Recipe component.

The Favorites component is a list that displays all items added to favorites with the add to favorites button.

function Favorites() {
  const [favList, setFavList] = useState(localStorage.getItem('favorites'));
  return (
    <FavPageContainer>
      {favList}
      {favList.map(listItem => {
        <li>{listItem}</li>
      })}
    </FavPageContainer>
  )
}
function Recipe() {
  let params = useParams();
  const [details, setDetails] = useState({});
  const [activeTab, setActiveTab] = useState('summary');
  const fetchDetails = async () => {
    const data = await fetch(`https://api.spoonacular.com/recipes/${params.name}/information?apiKey=${process.env.REACT_APP_API_KEY}`);
    const detailData = await data.json();
    setDetails(detailData);
  }
  
  useEffect(() => {
    fetchDetails();
  }, [params.name]);

  return (
    <DetailWrapper>
        <h2>{details.title}</h2>
        <img src={details.image} alt="" />

      <Info>
        <ButtonContainer>
          <AddToFavorites details={details}/>
          <Button 
            className={activeTab === 'summary' ? 'active' : ''}
            onClick={() => setActiveTab('summary')}>Nutrition Info
          </Button>
          <Button 
            className={activeTab === 'ingredients' ? 'active' : ''}
            onClick={() => setActiveTab('ingredients')}>Ingredients
          </Button>
          <Button
            className={activeTab === 'instructions' ? 'active' : ''}
            onClick={() => setActiveTab('instructions')}>Instructions
          </Button>
        </ButtonContainer>
        {activeTab === 'instructions' && (
          <div>
            <p dangerouslySetInnerHTML={{__html: details.instructions}}></p>
          </div>
        )}
        {activeTab === 'ingredients' && (
          <ul>
          {details.extendedIngredients &&
            details.extendedIngredients.map((ingredient) => (
              <li key={ingredient.id}> {ingredient.original}</li>
            ))}
          </ul>
        )}
         {activeTab === 'summary' && (
          <div>
            <p dangerouslySetInnerHTML={{__html: details.summary}}></p>
          </div>
        )}
      </Info>
    </DetailWrapper>
  )
}
function AddToFavorites(details) {
  const [active, setActive] = useState(false);
  const [favorites, setFavorites] = useState([]);
  const handleActive = () => {
      setActive(!active);
  };

  const handleFavorites = () => {
    handleActive();
    if(active){
      removeFromFavorites();
    } else if(!active) {
      addToFavorites();
  }
};

  const addToFavorites = () => {
    handleActive();
      setFavorites([...favorites, details]);
      console.log(favorites)
      localStorage.setItem('favorites', JSON.stringify(favorites));
  };
 
return (
  <AddToFavBtn className={active ? 'active' : null} onClick={handleFavorites}>
      {!active ? 'Add to favorites' : 'Remove from favorites'}
      <div>
      <BsFillSuitHeartFill/>
      </div>
  </AddToFavBtn>
)
}

What I’ve tried to do so far:

  • Pass API data to AddToFavorites as ‘details’ props in Recipe.
  • Set favorites as a state defaulting to an empty array in AddToFavorites.
  • Add onClick event to add to favorites btn in AddToFavorites, which calls AddToFavorites() function.
  • In the AddToFavorites() function I’ve tried to immutably update the favorites state by using the spread operator and adding the received details props (which has the API data) as a new item in the new copy of the state array, then store it in local storage.

What happens when I add an item to favorites is that I can add one item, and I can add it multiple times. But when I go to a new recipe to add another item, it deletes the old items and restarts.

I’ve been going over it and trying different things for a few days, but I can’t figure it out.

How to turn array of objects into one nested object?

I have an array of objects. Each object specifies the name of its parent (if any).

How can I turn this into one single object? I’m having trouble handling 2,3+ levels of nesting.

The output should look like this:

{ 
"REPORTING PERIOD": "2022",
"SIGNATURE DATE:" "20211055",
"HOUSE": 
  { "OWNER DATA":
    {"FIRST NAME": "Joe"},
    {"FIRST NAME": "Smith"}
  },
 {"VALUE HISTORY":
    {"INITAL PRICE": ...},
    {"LAST SALE PRICE": ...}
  },
 {"ADDRESS":
    {"STREET 1": ...},
    {"CITY": ...}
  },
}
"AGENT": 
 { etc. }
}

Input:

const data = [
    {
        "rank": 0,
        "key": "REPORTING PERIOD",
        "value": "2022",
        "parent": ""
    },
    {
        "rank": 0,
        "key": "SIGNATURE DATE",
        "value": "20211005",
        "parent": ""
    },
   
    {
        "rank": 0,
        "key": "HOUSE",
        "value": "",
        "parent": ""
    },
    {
        "rank": 1,
        "key": "OWNER DATA",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "FIRST NAME",
        "value": "Joe",
        "parent": "OWNER DATA"
    },
    {
        "rank": 2,
        "key": "LAST NAME",
        "value": "Smith",
        "parent": "OWNER DATA"
    },
    {
        "rank": 1,
        "key": "VALUE HISTORY",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "INITAL PRICE",
        "value": "12345",
        "parent": "VALUE HISTORY"
    },
    {
        "rank": 2,
        "key": "LAST SALE PRICE",
        "value": "1231236",
        "parent": "VALUE HISTORY"
    },
    {
        "rank": 1,
        "key": "ADDRESS",
        "value": "",
        "parent": "HOUSE"
    },
    {
        "rank": 2,
        "key": "STREET 1",
        "value": "5 MAIN TERRACE",
        "parent": "ADDRESS"
    },
    {
        "rank": 2,
        "key": "CITY",
        "value": "LONDON",
        "parent": "ADDRESS"
    },   
    {
        "rank": 0,
        "key": "AGENT",
        "value": "",
        "parent": ""
    },
    {
        "rank": 1,
        "key": "COMPANY DATA",
        "value": "",
        "parent": "AGENT"
    },
    {
        "rank": 2,
        "key": "COMPANY NAME",
        "value": "The Real Agent, Inc",
        "parent": "COMPANY DATA"
    },
    {
        "rank": 2,
        "key": "BUSINESS NUMBER",
        "value": "0021690080",
        "parent": "COMPANY DATA"
    },
    
    {
        "rank": 1,
        "key": "BUSINESS ADDRESS",
        "value": "",
        "parent": "AGENT"
    },
    {
        "rank": 2,
        "key": "STREET 1",
        "value": "800 MENLO STREET, SUITE 100",
        "parent": "BUSINESS ADDRESS"
    },
    {
        "rank": 2,
        "key": "CITY",
        "value": "MENLO PARK",
        "parent": "BUSINESS ADDRESS"
    },
    {
        "rank": 2,
        "key": "ZIP",
        "value": "94025",
        "parent": "BUSINESS ADDRESS"
    }  
]

I can get everything into one object but the nesting doesn’t work properly…Playground here

const resultObject: Record<string, unknown> = {};

//loop through array
for (const obj of data) {
    const { key, value, parent } = obj;

    if (parent === " " || parent === "" || parent === undefined) {
      resultObject[key] = value; // If parent is not specified, add it as a direct property in the result object
    } else {
        
      // If parent is specified, nest the element under its parent in the result object
      if (!resultObject[parent]) {
        resultObject[parent] = {}; // Create an empty object for the parent if it doesn't exist
      }
      (resultObject[parent] as Record<string, unknown>)[key] = value;
    }
  }
  console.log(resultObject);

AppScript Question – Deleting Duplicate File Names

Good evening,

I am trying to figure out a way to automatically delete files with duplicate filenames from a Google Drive folder. I’d like to keep the file with the oldest creation date, treat the others as duplicates, and delete the duplicates.

I feel close to achieving this using the below AppScript code (from this site: https://hackernoon.com/how-to-find-and-delete-duplicate-files-in-google-drive). The only issue is the code is seemingly keeping the newest file instead of the oldest one. Do you know what I can do to shift this code to keep the oldest file instead of the newest one? If you have other code in mind to achieve my goal, please also share that.

Thank you!


const FOLDER_ID = "INSERTIDHERE";

// Add id of the folder to check for duplicate

/*
 * Function looks for duplicate file names in designated folder and removes them.
 * @param {String} fileName
 */
function removeDuplicateFile() {
  let folder = DriveApp.getFolderById(FOLDER_ID);

  let files = folder.getFiles();

  let fileList = [];

  // if no file is found return null
  if (!files.hasNext()) {
    return;
  }

  // else
  while (files.hasNext()) {
    let file = files.next(),
      name = file.getName(),
      size = file.getSize();

    // checking this way always leaves first file not deleted
    if (isDuplicateFile(fileList, name, size)) {
      file.setTrashed(true);
    } else {
      fileList.push([name, size]);
    }
  }
}

/*
 * Function is helper function of removeDuplicateFile function.
 * It checks if theres already a file in the given lst with same name and size and returns true or false
 * @param {List} lst
 * @param {String} name
 * @param {Number} size
 * @returns {Boolean}
 */
function isDuplicateFile(lst, name, size) {
  for (let i = 0; i < lst.length; i++) {
    if (lst[i][0] === name && lst[i][1] === size) return true;
  }
  return false;
}


/*
 * Delete all the triggers if there are any
 */
var deleteTrigger = () => {
  let triggersCollection = ScriptApp.getProjectTriggers();
  if (triggersCollection.length <= 0) {
    console.log(`Event doesnot have trigger id`);
  } else {
    triggersCollection.forEach((trigger) => ScriptApp.deleteTrigger(trigger));
  }
  return;
};

/*
 * Create a trigger function for file which also deletes previous triggers if there are.
 */
function removeDuplicateFileTrigger() {
  // First Delete existing triggers
  deleteTrigger();

  // now remove duplicate files 
  removeDuplicateFile();
}

what is the best enviroment to develop a chrome extension ( vanilla – react -next ) [closed]

Vanilla

this method envolves building regular html css and js or ts files, and don’t provide the utilities present in some framworks like react or next, but is pretty easy to set up.

Framework (react-next)

this method envolves using the creat-react-app or create-next-app to generate a boiler plate that you need to export html output to a seperate folder with your manifest.json file by modifiying your build script in the package.json file. The biggest drawback of this method is that you have to run “npm run build” each time you want to test the extension. But you could develope your app on the browser as normal and when satisfied , you can export it to the extension folder.

JavaScript : what is the best way to write a function when working on code collaboratively [closed]

In the world of JavaScript, what is the best way to write a function in javascript (out of these 7 ways mentioned below)? I know it mostly depends upon the requirement. But from your experience what did you use most (with their pros and cons). Following are the options:

//function declaration

function result(x,y){

  return(x>y);

}

//function expression with let and const

let result = function(x,y){

return(x>y);

}

or

const result = function(x,y){

return(x>y);

}

//arrow function with let and const

let result = (x,y) => (x>y);

or

const result = (x,y) => (x>y);

//using a function expression using let and const

let result = function getResult(x,y){

return (x>y);

};

or

const result = function setResult(x,y){

return (x>y);

};

Exploring different ways of JavaScript functions and their utilities and trying to Find out the best for respective purpose of using.

TypeError: Cannot read properties of undefined when making AirTable API call with NextJS 13.4

I’m learning the new App router using NextJS 13.4 and have come across an error when attempting to make an external api call.

I actually receive all of the data properly from Airtable but because Next throws this error it causes my try catch to break. I cannot for the life of me figure out what is throwing this error and how to fix it.

Here is the error it is throwing:

- error TypeError: Cannot read properties of undefined (reading 'headers')
    at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/server/future/route-modules/app-route/module.js:254:61)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

I am making the initial call from a client component located at /app/dashboard/page.js.

page.js looks something like this:

'use client'

import React, { useEffect } from 'react';

const Dashboard = () => {

  useEffect(() => {

    async function fetchData() {
      try {
        const res = await fetch('/api/jobs', {
          method: 'GET',
          headers: {
            'id': '123'
          }
        });

        const data = await res.json();
        console.log("hello", data); // I never hit this console log because the catch below catches the error

      } catch (error) {
        console.log("error")
        console.log(error);
      }
    };

    fetchData();
  }, []);
  
  return (
    <div>
      Hello World
    </div>
  );
}

export default Dashboard;

Then I have my handler located at /app/api/jobs/route.js

route.js looks something like this:

import { NextResponse } from 'next/server';
import { jobs } from "../../../utils/Airtable";
 
export async function GET(req) {

  let headers = await req.headers.get('id'); // this logs out the headers just fine

  let recordsArr = [];

  try {
        jobs.select({
            view: 'Grid view',
            fields: ['Project Lead'],
            filterByFormula: `AND({Project Lead} = 'Jane Doe', AND({Created Day}, '6/1/23'))`
        }).eachPage(function page(records, fetchNextPage) {
                recordsArr.push(...records);
                
                try {
                    fetchNextPage();
                } catch (err) {
                    console.log(err)
                    return;
                };
            }, function done(err) { 

                if (err) {
                    return new NextResponse(JSON.stringify({message: 'Error'})) 
                };

                console.log(recordsArr) // this logs out the records perfectly
                return new NextResponse(JSON.stringify(recordsArr));
        });

    } catch (err) {

        return new NextResponse(JSON.stringify({message: 'Error'}), { status: 500 })

    };

  return new NextResponse(JSON.stringify(obj), { status: 200 });
}

As soon as I comment out the Airtable api call I stop getting the error.

If I move the entire Airtable API call to the client component it works flawlessly. So I think there is something about NextJS that I’m not comprehending.

Appreciate your time.

How to save a copy of a PDF generated with html2pdf on the server?

I’m creating a PDF using html2pdf and it’s generating and downloading fine on the client’s computer. However, I would like a copy of this PDF to be saved in a folder on the server. Does anyone have any ideas?

document.getElementById("download").addEventListener("click", function (e) {
  e.preventDefault();


  let pdfContainerElement = document.getElementById("pdf-container");
  let nomePdfElement = document.getElementById("nome");

  let opt = {
    margin: [0, 0, 0, 0],
    filename: "Declaração " + nomePdfElement.value + ".pdf",
    html2canvas: { scale: 3, letterRendering: true },
    jsPDF: {
      unit: "mm",
      format: "a4",
      orientation: "portrait",
    },
  };
  html2pdf().set(opt).from(pdfContainerElement).save();

  
});

Hey guys, I’m creating a PDF using html2pdf and it’s generating and downloading fine on the client’s computer. However, I would like a copy of this PDF to be saved in a folder on the server. Does anyone have any ideas?

ErrorException: Undefined array key “name” in file

I’m busy with a Client Management System which also keeps track of Client Orders. I’ve set up a CRUD API that handles the back-end database data reading and writing, but when I try to POST the cart data to the database I get the following errors.

quote Error parsing JSON from response: SyntaxError: Unexpected token ‘<‘, ” is not valid JSON

Receiving the following instead of valid JSON: <!– ErrorException: Undefined array key
“name” in file
C:UsersmjverOneDriveDocumentsCodingclient-apiroutesapi.php on
line 238

I’ve checked the input data into the $data[“name”] array on the client-side and I cannot see any errors. I’ve checked for typos and spelling errors and all the and I’m hoping a few fresh pair of eyes could help out.

My code snippets on the front-end and back-end are as follows:

calling the API call function in api.js:

async sendOrder(){
            console.log(this.cart);
            
            const order = await APIController.CreateOrder(this.cart.name, this.cart.qty, this.cart.option, this.cart.price, this.orderNum, this.cart.fee, this.cart.date, this.id);
            if(order){
                store.dispatch('clearCart');
            }
        },

The API call in the api.js file:

CreateOrder: (name, qty, option, price, orderNo, fee, date, userId) => {
        let responseClone;
        const csrfToken = document.cookie.match(/XSRF-TOKEN=([^;]+)/)[1];
        if(
            name == "" ||
            qty == "" ||
            option == "" ||
            price == "" ||
            orderNo == "" ||
            date == "" ||
            userId == ""
        ) {
            return false;
        } else {

            return fetch(API_BASE + "/orders/create", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-CSRF-TOKEN": csrfToken
                },
                body: JSON.stringify({ name, qty, option, price, orderNo, fee, date, userId })
            }).then((response) => {
                responseClone = response.clone();
                return response.json()
            })
            .then(data => {
                if(data.success){
                    alert("Order created successfully!")
                    return true;
                } else {
                    throw data.response.error;
                }
            }, (rejectionReason) => {
                console.log('Error parsing JSON from response: ', rejectionReason, responseClone);
                responseClone.text()
                .then((bodyText) => {
                    console.log('Receiving the following instead of valid JSON: ', bodyText);
                });
            }).catch(err => {
                alert(err);
            });
        }
    },

The php route in api.php file:

Route::post('/orders/create', function(Request $request){
    $data = $request->all();

    if(!Orders::where('orderNo', '=', $data['orderNo'])->exists()){
        $order = Orders::create([
            "name" => $data["name"],
            "qty" => $data["qty"],
            "option" => $data["option"],
            "orderNo" => $data["orderNo"],
            "userId" => $data["userId"],
            "price" => $data["price"],
            "fee" => $data["fee"],
            "date" => $data["date"],
        ]);

        if(empty($order->id)){
            return [
                "success" => false,
                "response" => [
                    "error" => "An unusual error has occured"
                ]
            ];
        } else {
            return [
                "success" => true,
                "response" => [
                    "order" => $order
                ]
            ];
        }
    } else {
        return [
            "success" => false,
            "response" => [
                "error" => "The inventory item already exists"
            ]
        ];
    }
});

My Orders Models file:

class Orders extends Model
{
    use HasFactory;

    protected $fillable = [
        'product',
        'qty',
        'option',
        'orderNo',
        'userId',
        'price',
        'fee',
        'date',
    ];

    public function product (){
        return $this->hasMany(Product::class);
    }
}

I would appreciate any help with this problem as I’ve been struggling with this for a while.

Hide element if specific id exists, even if it appears after page load

I have a simple image uploader with a ‘browse files’ button in the middle. I’m trying to hide this upload button once images have been selected so I can display the image thumbnails without the upload button crowding everything.

Here is what I have but it’s not working and i’m not sure what i’m missing (or if there’s a better way to do this altogether. I am open to a whole other method of doing this if there’s a better way, I just tried this because it seemed like a simple thing that should have done what I needed if I didn’t make a mistake. Any suggestions?

$(document).ready(function(){
if($('#thumbnail').length){
$('#submit').hide();}
});

I need this to always be “listening” for the images to show up because it could happen at any time. If it maters, this snippet is inside of a .js file…I wasn;t sure if that was my mistake or if that should have no effect on this. Ideally I’ll have an X on each image to remove the thumbnails and if all the images are removed then the upload button will appear again. I have no idea if what i’m attempting will do that as it is, or if I have to add a second bit to show it again.

Any help is appreciated. Thank you.

How do I set up for/in to add/remove CSS classes in JavaScript?

I am trying to make it so a class is removed on an onclick event to change the color. I tried using classList but that didn’t seem to work, and I was advised to use for/in. I tried looking for a guide online but everything was either not helpful or too confusing.

document.getElementsByClassName("fcbtnleftclicked").classList.remove("fcbtnleftclicked")
document.getElementById("dollarbtnleft").classList.add("fcbtnleftclicked")

With the CSS:

.fcbtnleftclicked, .fcbtnrightclicked{
  background-color: yellow;
}
.fcbtnleft, .fcbtnright{
  width: 125px;
  height: 50px;
  font-size: 20px;
  line-height: 3em;
  background-color: #F4F4F4;
}

How can I render Javascript from a called page?

Problem #1

I am writing a WebSite Engine. It does everything I want it to. But there is one factor missing. This factor is one of my 2 problems.

When I call onto an API or a PHP file, or whatever, it will not run this Engine on that page and then return it. It sends back the original, unchanged information I have already set up in it. Even though I put the API handling for this WebSite Engine in the called page, and I know the JSON has been updated, it won’t render the DOM changed before sending it back.

Problem #2

How can I make an import refresh itself from a Javascript module?

Problem #1

This is my API page:

<body></body>
<?php

        $div = [];
        $div["h"] = [
                "tagname" => "div",
                "textContent" => time()
        ];

        file_put_contents("test_timer.json", json_encode($div));

?>


<script type="module" src="./modals.js">
        import {forEachElem} from './modals.js';
        import time from './test_timer.json' assert {type: 'json'};
        forEachElem(time,document.body);
</script>

Problem #2

This is the full-engine code that creates it. ‘test_timer.json’ is a updating file that, when executed in this Javascript does not make a refreshed cite of it, but rather just uses the info from before.

import jsonElems from './modals.json' assert { type: 'json' };
import time from './test_timer.json' assert { type: 'json' };
var elemBody = document.getElementsByTagName("body")[0];

var count = 0;

function forEachElem(value, tempTag, root)
{
        if (root == undefined)
                root = tempTag;
        var temp = document.createElement(value["tagname"]);
        Object.entries(value).forEach((nest) => {
                const [k, v] = nest;

                if (v instanceof Object)
                        forEachElem(v, temp, root);
                else if (k.toLowerCase() != "tagname" && k.toLowerCase() != "textcontent" && k.toLowerCase() != "innerhtml" && k.toLowerCase() != "innertext")
                {
                        temp.setAttribute(k,v);
                }
                else if (k != "tagname" && k.toLowerCase() == "textcontent" || k.toLowerCase() == "innerhtml" || k.toLowerCase() == "innertext")
                {
                        (k.toLowerCase() == "textcontent") ? temp.textContent = v : (k.toLowerCase() == "innerhtml") ? temp.innerHTML = v : temp.innerText = v;
                }
        });
        if (root.childElementCount > 0)
        {
                tempTag.appendChild(temp);
//              tempTag.replaceChild(temp, tempTag.lastElementChild);
                //root.appendChild(tempTag);
                //tempTag.innerHTML = "";
//              root.innerHTML = tempTag.innerHTML;
        }
        else
        {
                root.appendChild(tempTag);
        }

}

// Put redundant timer on this forEachElem

forEachElem(jsonElems, elemBody);

test.html

<!DOCTYPE html>

<script src="pipes.js"></script>

<body>
<script>
        function didSomething(r)
        {
                console.log("HELLO!");
        }
</script>
<span id="hed" style="width:100%"></span><br>
Modals Demonstration - <dyn class="redirect" ajax="http://www.github.com/wise-penny/pipes"><u>GitHub</u></dyn> +
<dyn id="donate" class="redirect" ajax="https://paypal.me/thexiv"><u>Donate (I collect $1 donations. It's a hobby!)</u></dyn>
<timed id="timer1" delay="1000" ajax="./img_src.php" insert="hed"></timed>
</body>