ReactJS - Can i save specific rendered mapped data, to a variable? - javascript

I have data mapped in a component like this:
import React from "react";
import { useState } from "react";
import { useEffect } from "react";
import { get } from "lodash";
const Products = ({ data }) => {
return (
data.map((item, index) = > (
<div id={index}>
<img src={item.img} /> <br />
{item.name} <br />
{get(moreData, `[${item.name.toLowerCase()}].info[0]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[1]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[2]`)}
</div>
I want to be able to store this data:
{item.name}
{get(moreData, `[${item.name.toLowerCase()}].info[0]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[1]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[2]`)}
in a string, like string = {item.name},{moreData.item.name.toLowerCase().info[0]},...//etc
However you cannot declare variables inside of a component (as far as i know, still new to this).
I've tried .concat() - after each line and .push() with array instead of string:
{item.name} <br />
{dataString.concat(item.name)}
{dataArr.push(item.name)}
{get(moreData, `[${item.name.toLowerCase()}].info[0]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[1]`)}
{get(moreData, `[${item.name.toLowerCase()}].info[2]`)}
I was going to use DOM, but i've been told it's bad practice to use DOM in react.
I've also tried using state in the same way:
const [dataString, setDataString] = useState("");
...
{item.name}
{setDataString((dataString += item.name))}
But nothing seems to work as intended for me, and i'm out of ideas.
Edit:
I want to be able to copy the 'string/text' to clipboard eventually. So it can be imported to another site. Their required format is Item1, item1-info1, item1-info2, item1-info3, item2, item2-info1, item2-info2, item2-info3...etc

here is an example of how you can use the data object outside of the mapped objects.
also an example how to convert the data to an string in the format required.
because I don't know the structure of the data object of yours I just created an one, take this code and change it to the data structure
const data = [
{
name: "name1",
info1: "info1-1",
info2: "info1-2",
info3: "info1-3",
},
{
name: "name2",
info1: "info2-1",
info2: "info2-2",
info3: "info2-3",
},
{
name: "name3",
info1: "info3-1",
info2: "info3-2",
info3: "info3-3",
},
];
change this function to one that fits your needs.
const getStringOfData = (data) => {
let stringArray = [];
data.map((item) => {
stringArray.push(item.name);
stringArray.push(item.info1);
stringArray.push(item.info2);
stringArray.push(item.info3);
});
let stringResult = stringArray.join(",");
return stringResult;
};
useEffect(() => {
console.log("onMounte with useEffect");
let stringResult = getStringOfData(data)
console.log(stringResult);
}, []);
you can also call this function on onClick function depend in your requirement , the data object is available almost everywhere in your component

Continuing our discussion from the comments, it looks like there are two different things you are trying to do here:
display the UI to the user in a certain way
to be able to copy the information in a specific stringified format
I would advice to split these two behaviors, because they are totally unrelated to each other in terms of logic.
Start by aligning the information in your components how you need it.
const Product = ({ item, index }) => {
// do whatever you need to do with the information
// to display it correctly
const identifier = name.toLowerCase()
const infos = (moreData[identifier] && moreData[identifier].info) || {}
return (
<div>
{ info[0] && <p> info[0] </p>}
{ info[1] && <p> info[1] </p>}
{ info[1] && <p> info[2] </p>}
</div>
)
}
const Products = ({ data }) => {
const onClick = useCallback(
async () => {
// parse data and copy it using a separate
// util method.
const dataToString = /* actual implementation of the formatting to a string representation */
copyTextToClipBoard(dataToString)
},
[data]
)
return (
<div>
<button onClick={onClick} />
<div>
data.map((item, index) => (
<Product item={item} index={index} key={item.id} />
))
</div>
</div>
)
};
Now this would be your UI, the copyToClipboard method, is a bit of a hack, and looks like so:
const copyTextToClipBoard = (text) => {
const element = document.createElement('textarea');
element.style = { display: 'none' }; // eslint-disable-line
element.value = text;
document.body.appendChild(element);
element.select();
document.execCommand('copy');
document.body.removeChild(element);
};

Related

Building a reusable component to map through different sets of data React

Just like the title says I'm passing down pokemon data and rickandmorty data. I also happen to be using the tailwind select menu for react thats pretty long. Is there a better way to do it than conditionally map through the data? I know I can do this
{pokemons ? (
{pokemons?.map((pokemon, idx) => (
**30 line long code for the select menu**
))}
) : (
{rickAndMorty?.map((character, idx) => (
**Another 30 long line code for the select menu**
))}
)}
Is this the only way to do it or is there a cleaner way? Any help would be greatly appreciated. Thanks!
I suggest to try and separate any duplicated code out into some generic component, like:
const GenericSelectItem = (props)=>{
return (<>{/* props.itemValues */}</>);
};
const GenericSelectList = (props)=>{
const { selectItems } = props;
return (<SelectList>
{ selectItems.map( selectItem => <GenericSelectItem selectItem={ selectItem } /> ) }
</SelectList>);
};
const Example = (props)=>{
const itemsToDisplay = pokemons || rickAndMorty;
return (<>
{ !itemsToDisplay ? null : <GenericSelectList selectItems={ itemsToDisplay } /> }
</>);
};
In case the SelectItems are very different, add specific components, like:
const PokemonItem = (props)=>{
return (<GenericSelectItem>{/* pokemon specific variations */}</GenericSelectItem>);
};
const RickAndMortyItem = (props)=>{
return (<GenericSelectItem>{/* rickAndMorty specific variations */}</GenericSelectItem>);
};

How to transfer data from one file to another in reactjs

i have this array of objects, the thing that i am trying to do is to take the array from the file it's sitting in, and transfer it to another file where i can map the items. This is my code (the formatting and spacing is not on point) :
const Upload = () => {
const photos = [
{
id: new Date().getMilliseconds().toString(),
imagePath: "url..."
},
];
return (
<>
// Markup ....
</>
);
};
And i want to do something like this:
import photos from './COMPONENT_NAME';
const Func = () => {
return (
<> {photos.map((item) => <div> code... </div> )} </>
);
Is there any way to do it?
You can simply export them.
First, move the list outside of the Upload function and then put export key before the declaration like so.
export const photos = [
{
id: new Date().getMilliseconds().toString(),
imagePath: "url..."
},
];
const Upload = () => {
return (
<>
// Markup ....
</>
);
};
Then import them.
import { photos } from './COMPONENT_NAME';

How do I stop duplication of an object in localStorage?

I've a component call KeywordLocation.js, and It has one prop named location.
this component is a mapped array and on click I want to save the object of location in localStorage. I created here an empty array and pushing the object on every click. For now I'm getting 5 mapped location objects. when I click on any of them, it saves the object but on 2nd click it doesn't stop duplicating the object. How do I stop this duplication?
searchedLocation.map((location, i) => {
return (
<KeywordLocation
setShowMap={props.setShowMap}
location={location}
key={i}
getPositionFromManualSearch={props.getPositionFromManualSearch}
/>
);
});
KeywordLocation.js
const Component = ({ location }) => {
let allSearchedLocations = [];
const redirectToMap = async () => {
allSearchedLocations.push(location);
allSearchedLocations = allSearchedLocations.concat(
JSON.parse(localStorage.getItem("recent_location_searched") || "[]")
);
const previousLocation = JSON.parse(
localStorage.getItem("recent_location_searched")
);
console.log(previousLocation);
localStorage.setItem(
"recent_location_searched",
JSON.stringify(allSearchedLocations)
);
};
return (
<div onClick={() => redirectToMap()} className="pt-md cursor-pointer">
<p>{location.structured_formatting.main_text}</p>
<p className="text-xs border-b border-black pb-md ">
{location.description}
</p>
</div>
);
};
Are you entirely sure the duplication is ocurring on local storage?
As long as you use the same key, recent_location_searched, there will be only one value stored on that key. Take a look at the "Storage" tab on your browser's debug console to see what's actually being stored.
All evidence seems to point that the duplication is ocurring at the searchLocations variable, not atlocalStorage.
You might try to add some conditional logic that prevents you from pushing to searchLocations if the location is the same as the one on the last item on the array.
The problem is not related to localStorage but more about the usage of the array structure. You could rely on JavaScripts object to store the unique values. You lose the insertion order but you can create a companion array that keep a reference to the order.
const Test = ({ location }) => {
const redirectToMap = () => {
const locations =
JSON.parse(localStorage.getItem("recent_location_searched")) || {};
locations[location.name] = location;
localStorage.setItem("recent_location_searched", JSON.stringify(locations));
};
return (
<div onClick={() => redirectToMap()} className="pt-md cursor-pointer">
<p>{location.name}</p>
</div>
);
};
export default function App() {
const data =
JSON.parse(localStorage.getItem("recent_location_searched")) || {};
return (
<div>
<div className="App">
{[
{ name: "location1" },
{ name: "location3" },
{ name: "location2" }
].map((location) => (
<Test key={location.name} location={location} />
))}
</div>
<ul>
{Object.values(data).map((location) => (
<li key={location.name}>Saved {location.name}</li>
))}
</ul>
</div>
);
}

How to create a searchable componenent in React

Hi so I have some data that I want to search and filter through using hooks. I am able to get the initial list loaded but I don't know how to search the array and if the input text matches text in the array then display the array text that matches the input text. I tried using filter and stuff but am just confused as to how to put it all together. The data I need I can get by using props.data.map((e) => e.name)
import React, { useState } from "react";
import CharacterCard from "./CharacterCard";
import CharacterList from "./CharacterList";
export default function SearchForm(props) {
console.log(props.data.map((e) => e.name));
let hi = []
props.data.map((e) => hi.push(e.name))
const [search, setSearch] = useState('');
const check = () => {
if(search === ''){
return <CharacterCard data={props.data} />
} else if (hi.indexOf(search) === -1 ){
return <CharacterCard data={props.data} />
} else {
return <h1>{I want this to be anything that matches the input bot value}</h1>
}
}
let handleChange =(e) => {
setSearch(e.target.value)
}
return (
<section className="search-form">
<input type="text" onChange={handleChange} value={search} />
{check()}
</section>
);
}
You're on the right track, you just need to modify your check function. This example will render a list of all names that have a partial match in the beginning of the name:
const check = () => {
// get array of all names in `props.data`
const names = props.data.map(e => e.name);
// this will filter out any names that do not start with the content of `search`
// leaving us with an array of matching names
const searchMatches = names.filter(name => name.startsWith(search));
// do something with the results `searchMatches`
// (this example returns a list of names)
return <ul>{searchMatches.map(name => <li key={name}>{name}</li><ul>
})

Pre-populate Text Input With Object Values React

I'm trying to pre-populate text fields with pre-existing data in an object. I'm using a component renderer which is built from a switch statement like so (I'll remove the other cases to make it more readable):
switch (data.type) {
case 'text':
return (
<div
key={data.index}
data-name={selection}
>
<FieldLabel
htmlFor={data.name}
label={data.value}
/>
<FormInput
type={data.type}
name={data.name}
placeholder={data.placeholder}
onChange={
e => props.onCommentsChange(
e,
data.structure,
data.rules
)}
value={selection}
err={props.error === 1
&& props.validationErrors !== null
&& data.name in props.validationErrors
? 1
: 0
}
/>
</div>
)
}
The value of the input is the selection variable which is set like so:
let selection;
const data = {
type: props.type,
options: props.options,
name: props.name,
value: props.value,
structure: props.structure,
rules: props.rules,
index: props.index,
placeholder: props.placeholder,
order: props.order,
date: props.date,
onDateChange: props.onDateChange,
onFocusChangeHandler: props.onFocusChangeHandler,
focused: props.focused,
};
const section = Object.entries(data.structure)[0][0];
From this, I'm trying to use an if statement which loops through like so and sets the selection variable to the value:
if (props.usersCurrentSelection !== null && props.usersCurrentSelection !== undefined) {
console.log(props.usersCurrentSelection.data);
console.log(section);
console.log(props.usersCurrentSelection.data[section]);
if (section in props.usersCurrentSelection.data) {
Object.keys(props.usersCurrentSelection.data[section]).forEach(
item => {
let value = props.usersCurrentSelection.data[section][item];
console.log(value);
selection = value;
}
);
}
}
the console logs return the following:
As you can see it's finding the value relevant to it's key in the loop. And since the text component is taking the selection variable as its value I'm unsure as to why this isn't populating?
Thanks for the help.
This is some pseudo code to show the approach I would probably take, based on your recent comment:
const Input = props => {
// your switch statement goes here
}
const SectionFields = props => {
const { type, options, name, value, structure = {}, rules, index, placeholder, order, date, onDateChange, onFocusChangeHandler, focused } = props
const usersCurrentSelection = props.usersCurrentSelection || { data: {} }
const section = Object.keys(structure)[0]
const data = { type, options, name, value, structure, rules, index, placeholder, order, date, onDateChange, onFocusChangeHandler, focused }
const itemsInSection = Object.values(usersCurrentSelection)
return itemsInSection.length > 0
? (
<>
itemsInSection.map(([key, val]) => (
<Input data={data} item={val} />
))
</>
)
: null
}
const Form = props => (
<>
<SectionFields /*{props.section1 here}*/ />
<SectionFields /*{props.section2 here}*/ />
<SectionFields /*{props.section3 here}*/ />
</>
)
This is probably too simplistic for your needs, but it might point you in the right direction. It's still a little hard to be certain what the entire, finished form is supposed to look like, and how the underlying data is organized.

Categories

Resources