React dynamic page render by api response - javascript

I have an api that returns JSON with object array {component_name: string, component_data: any}. Based on this response render react page using function with switch-case by component_name and pass data to component in this case. I have many componets and this switch is very large. Can it be solved differently?
code snippet eg.
import Header from "./Header";
import Link from "./Link";
import Image from "./Image";
const pageComponents = (pageJson) => pageJson.map(p => {
switch(p.component_name) {
case: 'header': {
return <Header content={p.component_data.text} />
case: 'link': {
return <Link content={p.component_data.text} href={p.component_data.src} />
case: 'image': {
return <Image src={p.component_data.src} alt={p.component_data.alt} />
}
}
});
const EgPage = () => {
const pageJSON = [
{component_name: 'header', component_data: {text: 'Heder text 1'}},
{component_name: 'link', component_data: {text: 'Heder text 2', src: 'www.test.com'}},
{component_name: 'image', component_data: {src: 'www.test.img/1.jpg', alt: 'Test img'},
{component_name: 'header', component_data: {text: 'Heder text 2'},
]
return <div>{pageComponents(pageJSON)}</div>
}

Related

Warning: <Element /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements

I'm trying to automatically create React Elements from strings corresponding to the react-icons library. But I am getting the following errors in the console:
Warning: <RiHeartPulseFill /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
Warning: The tag <RiHeartPulseFill> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
Currently I have a data file that consists of a name and iconName (see below)
const categoriesData = [
{
name: 'Vitals',
iconName: 'RiHeartPulseFill',
},
{
name: 'Body',
iconName: 'RiBodyScanFill',
},
{
name: 'Sleep',
iconName: 'RiHotelBedFill',
},
{
name: 'Metabolism',
iconName: 'RiLungsFill',
},
{
name: 'Stress',
iconName: 'RiMentalHealthFill',
},
{
name: 'Strength & Training',
iconName: 'RiRunFill',
},
{
name: 'Lifestyle',
iconName: 'RiCellphoneFill',
},
]
export default categoriesData
I want to dynamically render React elements with the exact name as the iconName in the above datafile as React-icons require specific elements with those names.
Then I try to create a list of navigation links (using the React Router <Link> syntax and adding a React-icon + Name. See the code below:
const menuCategories = categoriesData.map((category) => {
const IconElement = category.iconName
return (
<Link
to={`/data/${category.name.toLowerCase()}`}
key={category.name}
className="flex flex-row items-center gap-2"
>
<IconElement />
{category.name}
</Link>
)
})
The issue I run into is the following error: Warning: <RiHeartPulseFill /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.
I does not seems to be incorrect as it actually IS PascalCase. However when I check dev tools I see the following: <riheartpulsefill></riheartpulsefill>
I have no Idea why this happens. Any solutions?
Extra: Does anyone know how I can also import those icon names based on the initial data files. I'm thinking about creating an icon selection tool, so only the selected icons should be imported from the react-icons lib.
If you want to dynamically render these icon components then you'll typically need to import and specify them in the config instead of strings corresponding to their names.
Example:
import {
RiHeartPulseFill,
RiBodyScanFill,
RiHotelBedFill,
RiLungsFill,
RiMentalHealthFill,
RiRunFill,
RiCellphoneFill,
} from "react-icons/ri";
const categoriesData = [
{
name: 'Vitals',
iconName: RiHeartPulseFill,
},
{
name: 'Body',
iconName: RiBodyScanFill,
},
{
name: 'Sleep',
iconName: RiHotelBedFill,
},
{
name: 'Metabolism',
iconName: RiLungsFill,
},
{
name: 'Stress',
iconName: RiMentalHealthFill,
},
{
name: 'Strength & Training',
iconName: RiRunFill,
},
{
name: 'Lifestyle',
iconName: RiCellphoneFill,
},
];
export default categoriesData;
const menuCategories = categoriesData.map((category) => {
const IconElement = category.iconName;
return (
<Link
to={`/data/${category.name.toLowerCase()}`}
key={category.name}
className="flex flex-row items-center gap-2"
>
<IconElement />
{category.name}
</Link>
);
});
An alternative is to create and export a lookup object for the icon components.
import {
RiHeartPulseFill,
RiBodyScanFill,
RiHotelBedFill,
RiLungsFill,
RiMentalHealthFill,
RiRunFill,
RiCellphoneFill,
} from "react-icons/ri";
export const iconMap = {
RiHeartPulseFill,
RiBodyScanFill,
RiHotelBedFill,
RiLungsFill,
RiMentalHealthFill,
RiRunFill,
RiCellphoneFill,
};
const categoriesData = [
{
name: 'Vitals',
iconName: 'RiHeartPulseFill',
},
{
name: 'Body',
iconName: 'RiBodyScanFill',
},
{
name: 'Sleep',
iconName: 'RiHotelBedFill',
},
{
name: 'Metabolism',
iconName: 'RiLungsFill',
},
{
name: 'Stress',
iconName: 'RiMentalHealthFill',
},
{
name: 'Strength & Training',
iconName: 'RiRunFill',
},
{
name: 'Lifestyle',
iconName: 'RiCellphoneFill',
},
];
export default categoriesData;
const menuCategories = categoriesData.map((category) => {
const IconElement = iconMap[category.iconName];
return (
<Link
to={`/data/${category.name.toLowerCase()}`}
key={category.name}
className="flex flex-row items-center gap-2"
>
<IconElement />
{category.name}
</Link>
);
});
To allow for any react-icons/ri icon then in the UI component import all of react-icons/ri and conditionally render the icon component if it exists.
import { Link } from 'react-router-dom';
import * as ReactRiIcons from "react-icons/ri"; // <-- all RI icons
import * as ReactRxIcons from "react-icons/rx"; // <-- all RX icons
const ReactIcons = { // <-- all merged icons set
...ReactRiIcons,
...ReactRxIcons
};
...
const menuCategories = categoriesData.map((category) => {
const IconElement = ReactIcons[category.iconName];
return (
<Link
to={`/data/${category.name.toLowerCase()}`}
key={category.name}
className="flex flex-row items-center gap-2"
>
{IconElement && <IconElement />} // <-- handle possible undefined icon
{category.name}
</Link>
);
});
...
Use React.createElement. Take a look here to see how: Create react component dynamically
Heres my recursive example:
const demoData = [
{
tagName: 'MyButtonComponent',
children: [
{
tagName: 'MyChildComponent'
}
]
},
{
tagName: 'MyOtherComponent'
},
]
function recursivelyRenderChildren(elements) {
if(elements.length) {
return elements.map((element, index) => {
return React.createElement(elementData.tagName, {
key: element.fieldType+'-'+index,
children: recursivelyRenderChildren(element.children)
});
})
}
}
const arrayOfElements = recursivelyRenderChildren(demoData)

Type error while fetching icons with Next.js icons - Typescript

Hello Everyone
I'm having a problem within my Typescript code, with my Next.js App.
i cannot find the right type to use with JSX rendering icons, that's fetched from database,
First part of code:
import * as bsIcons from 'react-icons/bs';
import * as faIcons from 'react-icons/fa';
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import { IconType } from 'react-icons/lib';
type option = {
id: number,
opt: string,
url: string,
logo: any,
sub: string[]
}
export const getStaticProps: GetStaticProps<{ options: option[] }> = async () => {
const res = await fetch('http://localhost:3000/api/menu')
const options: option[] = await res.json()
return {
props: {
options
}
}
}
here i had imported the icons libs from react-icons/bs & react-icons/fa then i defined the type of the response option
then i used the Nextjs getStaticProps function to import menu items from the api.
Second part of code:
<ul>
{ options.map(elm => {
let iconList = elm.logo.split('.')[0]
let menuIcon:IconType = elm.logo.split('.')[1]
let Logo: Function;
if(iconList == 'bsIcons'){
Logo = bsIcons[menuIcon]
} else {
Logo = faIcons[menuIcon]
}
return (
<Link key={elm.id} href={`${elm.url}`} style={{ textDecoration: 'none', color: 'powderblue' }}>
<li className={styles.option}>
<Logo style={{padding: '0 .5rem'}}/>
<p>{elm.opt}</p>
</li>
</Link>
)
})}
</ul>
in this part i'm trying to render the fetched data, according to my code, it's working perfectly, but at the VS Code it shows a typescript error at the if statement,
Error:
let menuIcon: IconType
Type 'IconType' cannot be used as an index type.ts(2538)
i don't have any clue of the type i must use at this part, so i imported the IconType from the icons lib it self, but still i cannot fix it.
is there any suggestions for this ?
a Part of fetched data:
{
id: 1,
opt: 'Dashboard',
url: '/dash',
logo: `faIcons.FaGripHorizontal`,
sub: []
},
{
id: 2,
opt: 'Content management',
url: '/content',
logo: `faIcons.FaWarehouse`,
sub: []
},
}

React JS: How to add multiple placeholder object inside components

Sorry this question might be duplicated, but none of the existing answers helped me
I'm a beginner in React and js
I want to add multiple objects inside the component
Like:
src={url}
name={text}
subTitle={subtext}
my index.js
const tableColumns = [
{
title: 'Title/Artist',
dataIndex: 'name',
key: 'name',
render: (text) => (
<div className="d-flex align-items-center">
<AvatarStatus
shape="square"
src="https://i.scdn.co/image/ab67616d00001e02bd26ede1ae69327010d49946"
name={text}
subTitle="Dua Lipa"
/>
</div>
),
},
];
return (
<>
<Table
className="no-border-last"
columns={tableColumns}
dataSource={recentReleasesData}
rowKey='id'
pagination={false}
/>
</>
my data.js
export const RecentReleasesData = [
{
id: '#5332',
artwork: 'https://i.scdn.co/image/ab67616d00001e02bd26ede1ae69327010d49946',
name: 'Future Nostalgia',
artist: 'Dua Lipa',
label: 'Warner Records',
barcode: '19029500',
releasedate: '2021-02-11',
tracks: '11',
promolink: 'Smart Link',
status: 'Approved',
},
{
id: '#6438',
artwork: 'https://i.scdn.co/image/ab67616d00001e02caf82abb2338880577e472be',
name: 'Love',
artist: 'Someonw',
label: 'UMG Records',
barcode: '50029500',
releasedate: '2017-08-21',
tracks: '2',
promolink: 'Smart Link',
status: 'Rejected',
},
];
My comp AvatarStatus.js
import React from 'react';
import PropTypes from 'prop-types'
import { Avatar } from 'antd';
const renderAvatar = props => {
return <Avatar {...props} className={`ant-avatar-${props.type}`}>{props.text}
</Avatar>;
}
export const AvatarStatus = props => {
const { name, suffix, subTitle, id, type, src, icon, size, shape, gap, text,
onNameClick } = props
return (
<div className="avatar-status d-flex align-items-center">
{renderAvatar({icon, src, type, size, shape, gap, text })}
<div className="ml-2">
<div>
{
onNameClick ?
<div
onClick={() => onNameClick({name, subTitle, src, id})}
className="avatar-status-name clickable">{name}
</div>
:
<div className="avatar-status-name"><a href="javascript:void(0)">
{name}</a>
</div>
}
<span>{suffix}</span>
</div>
<div className="text-muted avatar-status-subtitle">{subTitle}</div>
</div>
</div>
)
}
AvatarStatus.propTypes = {
name: PropTypes.string,
src: PropTypes.string,
type: PropTypes.string,
onNameClick: PropTypes.func
}
export default AvatarStatus;
https://reactjs.org/docs/components-and-props.html
components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
This function is a valid React component because it accepts a single “props” (which stands for properties) object argument with data and returns a React element. We call such components “function components” because they are literally JavaScript functions.
codepen example
I found the solution
index.js
render: (_, record) => (
<Flex>
<AvatarStatus
shape="square"
size={50}
src={record.artwork}
name={record.title}
subTitle={record.artist}/>
</Flex>
),

How to get deleted Image url in react-quill editor

I am using react-quill and I wan't to know how to select an image after being inserted into the editor and how to get the delted image url after being delted.
Here is my Editor Component
import React,{useState} from 'react'
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
const modules = {
toolbar:{
container: [
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline','strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
['link', 'image'],
['clean']
],
handlers:{
'image': async function(){
const editor=this.quill
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.addEventListener('change',(e)=>{
const url=awiat uploadFile(e.target.files[0))
const range = editor.getSelection(true);
editor.insertEmbed(range.index, 'image', url;
editor.setSelection(range.index + 1)
})
}
}
}
}
const formats = [
'header', 'font', 'size',
'bold', 'italic', 'underline', 'strike', 'blockquote',
'list', 'bullet', 'indent',
'link', 'image', 'color',
]
function Editor() {
const [editorData,setEditorData]=useState(" ")
const handleChange=(value)=>{
setEditorData(value)
}
return (
<div>
<ReactQuill formats={formats} modules={modules} value={editorData}
onChange={(data)=>handleChange(data)} />
</div>
)
}
export default Editor
So, how can i select a image inside the editor after being inserted and get the url after being deleted from the editor.
First, you should store your uploaded image URL inside an array like:
const [addresses, setAddresses] = useState([])
addresses.push(url)
Then when you submit the form, check if these addresses exist in your editor content save them, else any of them not exists delete it.
addresses.map(async (a) => {
if (!yourEditorValue.includes(a)) {
/* here you can add your method according to your backend, I am
using strapi */
const deletePhoto = await axios.delete(
`${yourServerUrl}/upload/files/${id}`,
{
//this one is not necessary if you don't need
headers: {
Authorization: `Bearer ${yourToken}`,
},
},
)
}
return true
})

How to render react component from a json object?

I made a menu component that accepts a json object with all menu itens.
For icons i use react-icons/io.
The json object is something like this:
const menus = {
Item1: { buttonText: 'Item 1 text', icon: { IoLogoAndroid }, path: 'item 1 path' },
Item2: { buttonText: 'Item 2 text', icon: { IoLogoAndroid }, path: 'item 2 path'},
};
This is the Menu function that will render the menu items as buttons:
const buttons = Object.keys(this.props.menus).map((menu) => {
return (
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{...this.props.menus[menu].icon} <- fail here
{this.props.menus[menu].buttonText}
</a>
);
})
I tried many ways to render the icon, but i am clueless on how this could work. Not sure if this is even possible. Any pointers?
If you are importing the icon from where you are defining the object then just tag it <IoLogoAndroid/>;, so react knows it should treat it as an element to render.
const menus = {
Item1: { buttonText: 'Item 1 text', icon: <IoLogoAndroid/> , path: 'item 1 path' },
Item2: { buttonText: 'Item 2 text', icon: <IoLogoAndroid/>, path: 'item 2 path'},
};
And then just call it directly (remove the ...)
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{this.props.menus[menu].icon}
{this.props.menus[menu].buttonText}
</a>
Alternatively you could just call React.createElement if you don't want to tag it in your object definition.
<a key={menu} href={this.props.menus[menu].path}
onClick={this.changeMenu.bind(this, menu)}>
{React.createElement(this.props.menus[menu].icon)}
{this.props.menus[menu].buttonText}
</a>
Here is a sample showing the 2 implementations https://codesandbox.io/s/pmvyyo33o0
I was just working with a similar project, and I managed to make it work, with a syntax like this
I here have an array of objects (like yours)
links: [
{
name: 'Frontend',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-web',
icon: <FaCode size={40} />,
id: 1
},
{
name: 'Backend',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-backend',
icon: <FaCogs size={40}/>,
id: 2
},
{
name: 'Mobile',
link: 'https://github.com/Thomas-Rosenkrans-Vestergaard/ca-3-app',
icon: <FaMobile size={40} />,
id: 3
}]
I then render my component by mapping, where I pass in the entire object as a prop
const projects = this.state.projects.map((project, i) => {
return(
<Project key={`Project key: ${i}`} project={project} />
)
})
I then use objectdestructing to get the prop
const { logo } = this.props.project
then it can just be displayed
//in my case I use antd framework, so I pass the FAIcon component in as a prop
<Meta
avatar={logo}
title={title}
description={description}
/>
I suppose you could do the same thing, by just passing the entire menu object in as a prop, and then accessing the icon?
You need to change the following:
icon: <IoLogoAndroid />
And in the code (remove the spread operator):
this.props.menus[menu].icon
Also, a few refactoring suggestions.
Do you really need to have an object of objects? Why not an array of objects, where every item has a "name" prop? It will be easier to iterate through, as you can access props directly from map, unlike with object keys.
You are creating a button list, so you should have ul and li tags aswell.
Consider passing only a reference to onClick such as:
onClick={this.changeMenu}
If you need to pass data around, you should use dataset for that. Pass a name/path then find it inside the change handler to avoid rebinding inside every re-render.
Refactored suggestion with an array of objects
changeMenu = e => {
const { menus } = this.props;
const { menuName } = e.target.dataset;
const menu = menus.find(menu => menu.name === menuName);
// Do something with menu
return;
};
renderMenu() {
return (
<ul>
{this.props.menus.map(menu => (
<li key={menu.name} style={{ listStyle: "none" }}>
<a
data-menu-name={menu.name}
href={menu.path}
onClick={this.changeMenu}
>
{menu.icon}
{menu.buttonText}
</a>
</li>
))}
</ul>
);
}

Categories

Resources