React - Output an SVG Component Dynamically - javascript

Im importing an SVG file as a ReactComponent.
I then want to output this component dynamically based on data from a request.
i.e.
import { ReactComponent as S1 } from '../../assets/images/characteristics/S1.svg';
{characteristics.map(characteristic => (
<div className="characteristic" key={characteristic.key}>
<characteristic.key />
</div>
}
where characteristic.key holds the name of the SVG i.e. "S1" in my example
How can i output the component as this does not work?
Thanks

You can try to do it like this instead:
import { ReactComponent as S1 } from "./path1.svg";
import { ReactComponent as S2 } from "./path2.svg";
// ...
function App() {
// defining characteristics for demonstration purposes
const characteristics = [
{ component: S1, key: "S1" },
{ component: S2, key: "S2" }
];
return (
<div className="App">
{characteristics.map(characteristic => (
<div className="characteristic" key={characteristic.key}>
<characteristic.component />
</div>
))}
</div>
);
}
So the string "S1" would be a good value for a key and you use S1 from the import to actually render the svg component.

Related

How to use images in the array in the react?

I am creating a website. I am a beginner. I have an issue. I have an array of react components. I don’t know can I use React components as the array elements. They are images, imported from the folder of my project. Also, I have an array of names of news companies. The idea is to create blocks with the name and image above. I want to create blocks according to the my images array length. So if the length of this array is 4, the cards I have 4. The issue is I can't display images, I imported them to my project. Main code is in the main page component. Also, I have a component called Author Card. In it, I have a React component, that receives name and image as the props and put them in the card Html block.
Here is my main page component code:
import React from 'react';
import AuthorCard from "./MainPageComponents/AuthorCard";
import BBC_Logo from '../assets/images/BBC_Logo.png';
import FOX_Logo from '../assets/images/FOX_Logo.png';
import CNN_Logo from '../assets/images/CNN_logo.png';
import ForbesLogo from '../assets/images/forbes-logo.png';
function MainPage(props) {
const channels = [
{
name: 'BBC',
index: 1
},
{
name: 'FOX',
index: 2
},
{
name: 'CNN',
index: 3
},
{
name: 'FORBES',
index: 4
},
];
const logos = [
<BBC_Logo key={1} />,
<FOX_Logo key={2}/>,
<CNN_Logo key={3}/>,
<ForbesLogo key={4}/>
];
return (
<div className="main-page">
<div className="main-page_container">
<section className="main-page_channels">
{channels.map( (channel) => {
logos.map( (logo) => {
return <AuthorCard name={channel.name} img={logo} />
})
})}
</section>
</div>
</div>
);
}
export default MainPage;
Here is my Author Card component code:
import React from 'react';
function AuthorCard(props) {
return (
<div className="author-card">
<div className="author-img">
{props.img}
</div>
<div className="author-name">
{props.name}
</div>
</div>
);
}
export default AuthorCard;
Please, help!
I would handle this a bit differently. First thing the way you import your logos is not imported as a component. Rather you get the path/src of the image which you can then use in a component. Read more about that here: https://create-react-app.dev/docs/adding-images-fonts-and-files/
So the way I would do this is to put the logo img src into your channels array and then pass that img src to the AuthorCard component. Then in the AuthorCard component your use a component to render the image. Like this:
import React from "react";
import BBC_Logo from "../assets/images/BBC_Logo.png";
import FOX_Logo from "../assets/images/FOX_Logo.png";
import CNN_Logo from "../assets/images/CNN_logo.png";
import ForbesLogo from "../assets/images/forbes-logo.png";
export default function App() {
return (
<div className="App">
<MainPage />
</div>
);
}
const channels = [
{
name: "BBC",
index: 1,
img: BBC_Logo
},
{
name: "FOX",
index: 2,
img: FOX_Logo
},
{
name: "CNN",
index: 3,
img: CNN_Logo
},
{
name: "FORBES",
index: 4,
img: ForbesLogo
}
];
function MainPage(props) {
return (
<div className="main-page">
<div className="main-page_container">
<section className="main-page_channels">
{channels.map((channel) => {
return <AuthorCard name={channel.name} img={channel.img} />;
})}
</section>
</div>
</div>
);
}
function AuthorCard(props) {
return (
<div className="author-card">
<div className="author-img">
<img src={props.img} alt="author card" />
</div>
<div className="author-name">{props.name}</div>
</div>
);
}
Here, we are using the map function to iterate over the channels array and render an AuthorCard component for each channel. We pass the name property to the AuthorCard component, as well as the corresponding logo from the logos array.
Note that we are also passing a key prop to the AuthorCard component to help React identify each component uniquely. In this case, we're using the index property of each channel object.

How to work with objects in an array in another document?

I need to display the title object from the array in the document App.js.
Everything works when I use an array without any objects :
(before)
App.js:
import React from 'react'
import TodoList from './Todo/TodoList'
function App(props) {
const mass = [
"text1",
"text2",
"text3"
]
return (
<div className='wrapper'>
<TodoList name={mass}> </TodoList>
</div>
)
}
export default App;
But when I add couple objects to array, there is an error (Objects are not valid as a React child (found: object with keys {completed, title}). If you meant to render a collection of children, use an array instead.)
(after)
App.js:
import React from 'react'
import TodoList from './Todo/TodoList'
function App(props) {
const mass = [
{completed: false, title: "text1"},
{completed: false, title: "text2"},
{completed: false, title: "text3"}
]
return (
<div className='wrapper'>
<TodoList name={mass}> </TodoList>
</div>
)
}
export default App;
TodoList.js:
import React from 'react'
import TodoItem from './TodoItem'
const styles = {
ul: {
color: 'red',
margin: 0,
padding: 0,
}
}
export default function TodoList(props) {
return (
<ul style={
styles.ul
}>
<TodoItem name2={props.name}/>
</ul>
)
}
TodoItem.js:
import React from 'react'
import App from '../App'
const styles = {
btn: {
background: 'none',
marginLeft: '30px',
},
divv: {
listStyleType: 'none',
margin:'10px',
marginLeft: '10px',
border:'1px solid #ccc',
padding:'5px',
width: '250px'
}
}
const TodoItem = (props) => {
return (
<div>
{
props.name2.map((comp, momp) =>
{
return (
<div style={styles.divv}>
<li>
<input type="checkbox" /> {momp +1} Test {comp} <button style={styles.btn}>×</button>
</li>
</div>
)
}
)
}
</div>
)
}
export default TodoItem
So in the TodoItem doc I want to work with object named 'title' from array, but not with an array as the whole
try to use comp.title instead of comp in your TodoItem.js file
when you use array with string items in map function you will see only string (items of array)
but when you use object in your array in map function you will see objects as item

How to fix 'Objects are not valid as a React child'

Codesandbox link.
I'm getting this error when trying to use filter() through a big array of objects (defined as 'filteredCharacters'), and render only those match the id of '6' to the screen (only one does).
I console.log(filteredCharacters), and I can clearly see in console that it works. But for some reason, I'm getting a "Objects are not valid as a React child" error thrown.
The code below is from /components/content.js, in the Codesandbox link above.
import React, { Component } from 'react';
import Intro from '../intro/intro';
class Content extends Component {
render() {
// Grab the 'characters' object from App.js, and assign it to 'this.props'
const { characters } = this.props;
// Filter the chracters and return only whose 'id' belongs to that of '6'
const filteredCharacters = characters.filter(characters => {
if (characters.id === 6) {
return (
<div className="characters" key={characters.id}>
<p>Name: {characters.Name}</p>
<p>ID: {characters.id}</p>
<p>Job: {characters.Job}</p>
<p>Age: {characters.Age}</p>
<p>Weapon: {characters.Weapon}</p>
<p>Height: {characters.Height}</p>
<p>Birthdate: {characters.Birthdate}</p>
<p>Birthplace: {characters.Birthplace}</p>
<p>Bloodtype: {characters.Bloodtype}</p>
<p>Description: {characters.Description}</p>
</div>
)
}
});
// Check to see if it logs properly (it does)
console.log(filteredCharacters);
// When trying to render this to the screen below, it doesn't work
return (
<div>
{filteredCharacters}
</div>
)
}
}
export default Content;
filter will only create a new array with all the elements that returned a truthy value from the function.
You can instead use filter first to get the relevant characters, and then use map on the new array to get the JSX you want to render.
const filteredCharacters = characters
.filter(character => character.id === 6)
.map(character => (
<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>
));
Adding to #Tholle's answer, you could combine those operations into one with reduce
const filteredCharacters = characters
.reduce((acc, character) => {
if (character.id !== 6) return acc;
acc.push(<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>);
return acc;
}, []);
Currently you are using one simple object to behave as a node in the HTML structure of the component you are writing. One of the best practices to use react in such cases is to create and then call this as a react component itself.
Following is your code that now has a separate component that can be called on need:
import React, { Component } from 'react';
import Intro from '../intro/intro';
const FilteredCharcters = characters => {
characters.filter(character => {
if (character.id === 6) {
return (
<div className="characters" key={character.id}>
<p>Name: {character.Name}</p>
<p>ID: {character.id}</p>
<p>Job: {character.Job}</p>
<p>Age: {character.Age}</p>
<p>Weapon: {character.Weapon}</p>
<p>Height: {character.Height}</p>
<p>Birthdate: {character.Birthdate}</p>
<p>Birthplace: {character.Birthplace}</p>
<p>Bloodtype: {character.Bloodtype}</p>
<p>Description: {character.Description}</p>
</div>
)
}
});
class Content extends Component {
render() {
const { characters } = this.props;
return (
<div>
<FilteredCharacters characters={characters} />
</div>
)
}
}
export default Content;

Lodash to sort data in react

I am trying to order by events based on their date. I have a button that will render the events in ascending order and another button will render the events in descending order. I am using lodash's orderBy method. I am defining the orderDir in the state of my container component. The orderDir changes when the buttons get clicked. The orderBy should be the 'start' property of my data object. I believe I can get to access it. Here is my code:
import React, { Component } from 'react';
import logo from './logo.png';
import './App.css';
import EventCard from './EventCard';
import SampleData from './data/sample-data.json';
import _ from 'lodash';
let dataRaw = SampleData.data.eventSearch.edges;
let data= dataRaw;
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: {data},
defaultSortIndexes: [],
orderDir:'desc'
};
this.sortEevent = this.sortEevent.bind(this);
}
sortEevent(e, type){
if (type === 'asc'){
this.setState({ orderDir:'asc'});
}else{
this.setState({ orderDir:'desc'});
}
}
render() {
let filteredEvents = this.state.data.data;
let orderDir = this.state.orderDir;
console.log(filteredEvents);
filteredEvents = _.orderBy(filteredEvents, (event)=>{
return event['start'];
}, orderDir);//order by
let events = filteredEvents.map((event, i) =>
<EventCard
key={i}
name={event.node.title}
image={event.node.painting.images[0].thumb_url}
venue={event.node.venue.name}
tickets={event.node.tickets_available}
distance={event.distance}
date={event.node.start}
firstName={event.node.artist.first_name}
lastName={event.node.artist.last_name}
artistImage={event.node.artist.images[0].thumb_url}
/>
);//map
console.log(this.state.data)
return (
<div className="App">
<div className="header-wrapper">
<div className="logo-header">
<div className="logo-wrapper">
<img src={logo} className="App-logo" alt="logo" />
</div>
<div className="menu-wrapper">
<a>Events</a>
</div>
</div>
<div className="filters-wrapper">
<div className="filters">
<p>Search Filters:</p>
<button onClick={(e) => this.sortEevent(e, 'asc')} className="green">SORT ASCENDING</button>
<button onClick={(e) => this.sortEevent(e, 'desc')}>SORT DESCENDING</button>
</div>
</div>
</div>
<div className="EventList">
{events}
</div>
</div>
);
}
}
export default App;
I am console logging the filteredEvents, which is the object that I am trying to sort. Here you can see where the 'start' property is.
Thanks so much in advance!
The value of the start element in your object is a string, and not a Date object, so the comparison will be done using strings comparison and not dates.
You can convert the strings to date using:
new Date(event.node.start)
here is the orderBy usage:
filteredEvents = _.orderBy(filteredEvents, (event)=>{
return new Date(event.node.start);
}, orderDir);//order by

i18n for array elements of react component

I would like to use universe:i18n for translating my meteor application (using react).
In this component you can see, that I iterate through an array using map() and as the output I would like to get the categories as translations:
imports/ui/components/example.jsx
import React, { Component } from 'react'
import i18n from 'meteor/universe:i18n'
class Example extends Component {
getCategories(index) {
const categories = [ 'one', 'two', 'three' ]; // <-- Get correct translations of these elements
return categories[index - 1];
}
render() {
return (
<div id="content">
{ this.props.sections.map((i) => {
return (
<div>
{ this.getCategories(i.index) }
</div>
);
}) }
</div>
);
}
}
i18n/de.i18.json
{
categories: {
one: 'Eins',
two: 'Zwei',
three: 'Drei'
}
}
I tried to do it with
const T = i18n.createComponent()
class Example extends Component {
getCategories(index) {
const categories = [ 'one', 'two', 'three' ]; // <-- Get correct translations of these elements
return categories[index - 1];
}
render() {
return (
<div id="content">
{ this.props.sections.map((i) => {
return (
<div>
<T>categories[{ this.getCategories(i.index) }]</T>
</div>
);
}) }
</div>
);
}
}
It won't work, because you have to use dot instead of bracker notation, so
<T>categories.{ this.getCategories(i.index) }</T>
Instead of
<T>categories[{ this.getCategories(i.index) }]</T>
But it still won't work, because it will create an children array, but only string is accepted, so use it like this:
<T children={`categories.${ this.getCategories(i.index) }`} />
Source.

Categories

Resources