ES6 module immutability - ecmascript-6

I thought ES6 module exports were always immutable, so I'm pretty confused about the behaviour I'm getting. I have a simple array of colors that I would like to use in multiple components of my Vue application. It is in it's own file like so:
export const colors = [
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]
Then I import it into the component where I want to use it like this:
import { colors } from '../assets/colors';
I have a function for picking a random color and then removing it from the list so it isn't picked again for the same component. It's something like this.
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = colors[colorIndex];
colors.splice(colorIndex, 1);
});
The idea here is to pick a random color from the list, assign it a description and then remove it from the list so a different one is picked on the next iteration of the forEach.
The problem is that it seems to be removing the colors from the list permanently. So when I import and try to use the array in another component, there are no colors in it. How can I make it so there is a fresh instance of the colors array for every component?

The imported bindings are not assignable, that's all. They are similar to const - you cannot change the variable, but you can mutate the object it holds. To prevent that, freeze the object when exporting it:
export const colors = Object.freeze([
'#ffb3ba',
'#ffdfba',
'#ffffba',
'#bae1ff',
]);
How can I make it so there is a fresh instance of the colors array for every component?
Have a look at Copying array by value in JavaScript for that: just colors.slice(). Also you'll want to check out How to randomize (shuffle) a JavaScript array? for how to efficiently get the random colors for your descriptions - there are even some answers that do not mutate the input.
import { colors } from '../assets/colors';
import { shuffle } from '…';
const randomColors = shuffle(colors.slice());
console.assert(descriptions.length <= randomColors.length);
for (const [i, description] of descriptions.entries())
description.color = randomColors[i];

ES6 module imports are not immutable, as you have correctly observed.
You could create a shallow copy of the array and operate on that one:
const copiedColors = [...colors];
descriptions.forEach(item => {
const colorIndex = Math.floor(Math.random() * colors.length);
item['color'] = copiedColors[colorIndex];
copiedColors.splice(colorIndex, 1);
});

Related

Manage to access data and send from different components and handle them

I'm trying to create a layer where I can handle different actions from users.
From example, when you click a div, creates an layer of type 1 and then sends it to the layer component to start showing a list of dropdown (for example).
As I'm new in react, I've did something, but it thrown me tons of errors and it doesn't seems to work.
This is what I've build: https://codesandbox.io/s/laughing-agnesi-47suvp
What I want is this:
When you click a .group-item (from Header component) save as an object the ID of the player which .group-item you clicked correspond (also the clientX and clientY of the .group-item) and then pass a function to display the .dropdown inside layers component which will also have the data you stored from the Header component (ID, clientX, clientY)
There are indeed several mistakes:
In some places you access layer.type, but you create a layer with property layerType instead; make sure to be consistent
when you add a new layer, make sure to build an array:
const addNewLayer = (object) => {
listLayers((layerObject) => ([ // Note the square brackets, instead of curly braces
...layerObject, // Previous array of layers
object // New layer to be added
]));
};
In Layers.js, do not change a state directly at the root of your function component body: this creates an infinite rendering loop (the function re-runs whenever its state changes); in your case, you can wrap it in a useEffect that depends on layers prop:
useEffect(() => {
layers.forEach((l) => {
if (l.layerType == 1) {
console.log("layer type 1 found! .. set .dropdown to be seen");
setLayerActive(true);
}
});
}, [layers]); // Re-run only if the list of layers changes
Demo: https://codesandbox.io/s/quiet-browser-iiip34

I am displaying dynamic data on this React Boostrap Slider. Is there a way to make the code more efficient?

this is my first post here. I am building a React Boostrap Carousel that pulls Movie data from the database and displays it. I am new to React and programming in general. So far i made the code work. But i do not know how to handle the images. The images are stores in React **src/assets/imgs. **. Should i store a reference to the image in the database like so ../../assets/imgs/the-batman.jpg and then display it? If so later on on the project the admin will have to create a MovieOfTheMonth. He should be able to input movie title, descrition etc, and also upload a movie image. Is there a way when the image is uploaded it, to store it to a specific folder, in this case src/assets/imgs and also create a reference in the database? I do not need the solution here, just to tell me if it is achievable. Finally is there a way to improve my code?
this is my full code for this component
import React, {useState, useEffect } from 'react';
import './Carousel.css'
import Carousel from 'react-bootstrap/Carousel';
import 'bootstrap/dist/css/bootstrap.min.css';
import axios from 'axios';
const CarouselHero = () => {
//boostrap code
const [index, setIndex] = useState(0);
const handleSelect = (selectedIndex, e) => {
setIndex(selectedIndex);
};
//Get Movies of the month
const [movie, setMovie] = useState([])
const getMovie = () => {
axios.get("http://localhost:4000/moviesOfTheMonth")
.then((res) => {
const myMovie = res.data
myMovie.push()
setMovie(myMovie);
})
}
useEffect(() => getMovie(), []);
return (
<Carousel activeIndex={index} onSelect={handleSelect} fade>
{movie.map((item) => {
const {id, title, description} = item.Movie
return (
<Carousel.Item interval={2000}>
<img
src={require("../../assets/imgs/the-batman.jpg")}
alt="First slide"
/>
<Carousel.Caption >
<h1>{title}</h1>
<p>{description}</p>
<button>Book Now</button>
</Carousel.Caption>
</Carousel.Item>
)
})}
</Carousel>
);
};
export default CarouselHero;
I think technically it is achievable to iterate over the assets folder and create database entries for new images (create and compare hash?), but it is usually not how you do it. I would put images in some file storage like S3 and reference them with id.
I don't know who the admin will be in your project, but if admin is rather a non technical person, you could create (or use a template of course) a small and simple admin dashboard, where he/she can maintain a movie of the month via UI.
FFinally some remarks on your code:
const handleSelect = (selectedIndex, e) => { setIndex(selectedIndex); }; - If you need only first, but not second, third etc. argument, you can just leave it out: (selectedIndex) => ...
const [movie, setMovie] = useState([]) - don't forget to use semicolon after every statement. (They are optional, but are useful sometimes to avoid weird errors). Also, you have a list here. So maybe better call it "movies".
myMovie.push() - What are you trying to push here?
useEffect(() => getMovie(), []); - Usually you define and call async function directly in useEffect. Don't you get any hints or warning?
movie.map((item) => { - When you iterate and get a list back React needs a key on every element (here on Carousel.Item). Don't just use the index, as it is a bad practice. Always try to find id property in your data.
const {id, title, description} = item.Movie - Why is the data nested by Movie object? Can't you just say item.id, item.title, item.description?

Is it possible to copy an object's properties from one component and display them in another component using refs?

Say I have 2 components. One is a table with a list of stores. Each store has properties like color, item, open, closed. The other component is one to create a store.
I want to be able to click on a little copy icon on one of the created stores already, and take that information to the create store component, and populate that component with the properties in order to make changes and create a completely new store.
Is this doable using refs? Or is there a better way of doing this?
Use ref to this task is a mistake. React works using a Virtual DOM that is a cleaner and faster Object Tree with information that will be through to DOM by React DOM the REF API is used to access direct the DOM information, and you don't need any information from DOM to do ur task.
https://reactjs.org/docs/refs-and-the-dom.html
A way yo do what you describe is create a state/setState on the parent component and pass a state for the store component and a setState to the table component for example:
import React, { useState } from 'react'
const StoreComponenet = ({ color, item, open})=>{
// logic of component
return (
<div>
// ...
</div>
)
}
const TableComponent = ({ setStore })=>{
// logic of component
return (
<table>
<tr onClick={() => setStore("blue", {id: 2, name: "BlueStore" }, false)}>
Build blue store
</tr>
...
</table>
)
}
const App = ()=>{
const [store, setStore] = useState(null)
return (
<TableComponent setStore={setStore} />
{
store &&
<StoreComponent
color={store?.color}
item={store?.item}
open={store?.open}
/>
}
)
}

Read long text in Angular 2

I have a very long document - 40000 words - I would like to display in a styled manner, like html.
I need to display it with headers, paragraphs and bold styling.
I am building an Angular app. I tried loading the converted document as a local html, but it takes a very long time.
For instance, I tried this:
var html = this.http.get("../data.html").map(ref => {
console.log(html);
} );
Are there any other ways I can load this text? Maybe break it up into smaller chunks somehow?
Based on what you've provided with no other context:
You need to subscribe to the Observable otherwise, nothing will ever happen since Observable execution is lazy:
var html = this.http.get("../data.html")
.map(ref => {
console.log(html);
return ref;
})
.subscribe(ref => ...);
Also, you're using console.log(html) in your map, but html does not exist in the context of map so you would need to do something like:
var html = this.http.get("../data.html")
.map(ref => {
console.log(ref); // Does this log appear and what does it contain?
return ref;
})
.subscribe(ref => ...);
Finally, var html is an Observable not HTML so I'd probably rename this to something a bit more descriptive if you're passing it around and subscribing to the response:
const data$ = this.http.get("../data.html")
.map(ref => {
console.log(ref);
return ref;
});
// ... do other stuff
data$.subscribe(ref => ...);
Or if not passed chain it and subscribe which indicates the Observeable has completed:
this.http.get("../data.html")
.map(ref => {
console.log(ref);
return ref;
}).subscribe(ref => ...);
If this doesn't help answer the question it's because you haven't provided enough information, and I'd suggest answering:
In the second example what does console.log(ref) output?
Include more code that provides more context like do you use subscribe already and what does the data you're using look like?
Make an example in StackBlitz that replicates the issue. Just click Angular and you get a pre-made Angular application you can drop your code into and then people can hack directly on the issue. Takes tops 5 seconds to setup

Immutable.js without redux, how do I create new lists without explicitly naming them?

---Background---
I'm trying to learn current best data management practices and as far as I can tell that means stateless / stateful components and immutable data structure. I'm having problems with implementing latter (immutables). I'm trying to incorporate it into angular 2 without redux. Redux is on my list of things to learn but for now I want to use immutable.js without redux.
---The problem---
How do I create a copy of an array in a service and return it on demand? I have this example code (just for illustration purposes, I haven't tested it!):
import { Product } from './product';
import { Immutable } from './immutable';
export class ProductListService {
let id = 0;
const cheese = new Product(id++, 'cheese');
const ham = new Product(id++, 'ham');
const milk = new Product(id++, 'milk');
// I fill the list with some sample data
let oldProductList = Immutable.List.of(cheese, ham, milk);
let newProductList = [];
let returnProductList = oldProductList;
getProductList() {
return returnProductList;
}
addProduct() {
// As far as I know, this creates a deep immutable copy
newProductList = oldProductList.withMutations(function (list) {
list.push(new Product(id++, 'name'););
});
returnProductList = newProductList;
oldProductList = newProductList;
}
}
The above code is based on the example from the official docs where they just add a number to the variable each time they create a copy (I understand that is only for example purposes?). How do I go about creating new lists without using numbers? Do I use oldList / newList? Do I dynamically create new numbers for new variables so that I have a history of objects?
I feel I'm doing something wrong on a architectural level here. What is the correct approach? All immutable.js examples are using redux or show no real-life example, does someone know of a good material to learn about immutalbe.js (+ possible ng2?)
Thanks
Not sure i fully understand what you want to do here,
but consider this:if you just want to push one element to the list you should not use withMutations.
let list1 = Immutable.List(['one'])
let list2 = list1.push('two')
console.log(list1.toJS()) // ['one']
console.log(list2.toJS()) // ['one', 'two']
Applying a mutation to create a new immutable object results in some overhead, which can add up to a minor performance penalty. use withMutations only If you need to apply a series of mutations locally before returning
let list1 = Immutable.List(['one'])
var list2 = list1.withMutations(function (list) {
list.push('two').push('three').push('four').push('five');
});
console.log(list1.toJS()) //["one"]
console.log(list2.toJS()) //["one", "two", "three", "four", "five"]
here we create a temporary mutable (transient) copy of list 1 and apply a batch of mutations in a performant manner by using withMutations
I hope that answers your question