Pass component as a prop in Reactjs - javascript

I got an article showing how to pass a component as a prop, but I could not make it works, can anyone help me on it?
This is the example I got.
import React from "react";
import Foo from "./components/Foo";
import Bar from "./components/Bar";
const Components = {
foo: Foo,
bar: Bar
};
export default block => {
// component does exist
if (typeof Components[block.component] !== "undefined") {
return React.createElement(Components[block.component], {
key: block._uid,
block: block
});
}
}
And this is my code
I have one file called routes.js, that has the state called routes.
var routes = [
{
path: "/user-profile",
name: "Quem Somos",
icon: "FaIdBadge",
component: UserProfile,
layout: "/admin"
}
And another component called Sidebar, where I receive routes and need to change the icon based in what is configured at the 'routes' prop.
const Components = {
fa:FaIdBadge
}
<i>{prop => Components(prop.icon)}</i>
But the property with the icon is not recognized.

You're pretty close.
Choosing the Type as Runtime
import React from 'react';
import { PhotoStory, VideoStory } from './stories';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}
So more specific to your example
// component map in the file for lookup
const components = {
fa: FaIdBadge,
}
...
// In the render function
// fetch the component, i.e. props.icon === 'fa'
const IconComponent = components[props.icon];
...
<IconComponent />

Related

Navigo Javascript routing: Every route I register didn't match any of the registered routes

I'm learning how to make a single page app with javascript.
My javascript teacher provided a beautiful tutorial how to create a single page application from scratch. I followed the tutorial and everything went well untill the part where the routing came in..
He uses a library which is called navigo. I don't know why but it seems to not working for me at all.
The moment I've written the final line of code. My homepage disappeared and the console gave a warning that my route '/' which is my homepage, didn't match any of the registered routes, but it looks like there is no route registered at all, while I'm definitly registering them..
here is my code
My root index.js
import './sass/main.scss';
import App from './App';
import { HomeComponent, NewEventComponent } from './Components';
// Retrieve appComponent
const initApp = () => {
const appContainer = document.getElementById('appContainer');
const app = new App(appContainer);
app.addComponent(new HomeComponent());
app.addComponent(new NewEventComponent());
};
window.addEventListener('load', initApp);
My App.js (here is where my route is defined for every component. routerPath makes it dynamic )
// The App Wrapper
import Component from './lib/Component';
import Router from './Router';
class App {
constructor(parent) {
this.parent = parent;
this.components = [];
}
clearparent() {
while (this.parent.firstChild) {
this.parent.removeChild(this.parent.lastChild);
}
}
addComponent(component) {
if (!(component instanceof Component)) return;
// get the name from our component
const { name, routerPath } = component;
// when a component asks to reRender
component.reRender = () => this.showComponent(component);
// add to internal class
this.components.push(component);
// add to router
Router.getRouter().on(routerPath, () => {
this.showComponent({ name });
}).resolve();
}
showComponent({ name }) {
const foundComponent = this.components.find((component) => component.name === name);
if (!foundComponent) return;
this.clearparent();
this.parent.appendChild(foundComponent.render());
}
}
export default App;
The Home Component
// The Home Component
import Component from '../lib/Component';
import Elements from '../lib/Elements';
class HomeComponent extends Component {
constructor() {
super({
name: 'home',
model: {
counter: 0,
},
routerPath: '/',
});
}
incrementCounter() {
this.model.counter += 1;
}
render() {
const { counter } = this.model;
// create home container
const homeContainer = document.createElement('div');
// append header
homeContainer.appendChild(
Elements.createHeader({
textContent: `Current value is: ${counter}`,
}),
);
// append button
homeContainer.appendChild(
Elements.createButton({
textContent: 'increase',
onClick: () => { this.incrementCounter(); },
}),
);
return homeContainer;
}
}
export default HomeComponent;
A Component
// My components
class Component {
constructor({
name,
model,
routerPath,
}) {
this.name = name;
this.model = this.proxyModel(model);
this.routerPath = routerPath;
this.reRender = null;
}
proxyModel(model) {
return new Proxy(model, {
set: (obj, prop, value) => {
obj[prop] = value;
if (this.reRender) this.reRender();
return true;
},
});
}
}
export default Component;
The Router
// My Router
import Navigo from 'navigo';
const Router = {
router: null,
getRouter() {
if (!this.router) {
const rootUrl = `${window.location.protocol}//${window.location.host}`;
this.router = new Navigo(rootUrl, false);
}
return this.router;
},
};
export default Router;
Solution: I switched to Navigo(^7.0.0) and it works!
I seem to have the same problem as you. I'm also using navigo (^8.11.1). The problem is fixed for me when I declare a new router like this: new Navigo('/', false).
It still gives me the warning now, but it loads the page. sadly, this will only work in a dev environment

Unexpected token in PropTypes (expected ",")

In the following code:
import React from 'react';
import {
Avatar, Menu, MenuItem, Tooltip, Box,
} from '#mui/material';
import PropTypes from 'prop-types';
// eslint-disable-next-line no-unused-vars
import { positions } from '#mui/system';
const menuItems = ['Test', 'Yes', 'No'];
function PositionedMenu(props) {
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
const { boxStyle: PropTypes.object} = props;
...
I am getting the following error in the specific position - PropTypes.object (where I validate boxStyles):
Parsing error: Unexpected token, expected ",".
Why is this happening? I'm using React with eslint set to airbnb options.
You can pass boxStyle parent to child component and use it.
// Parent component
function ParentComponent(props) {
render(
<childComponent boxStyle={{ background: 'red'}} />
)
}
// Child Component
function childComponent(props) {
const { boxStyle } = props;
const [anchorEl, setAnchorEl] = React.useState(null);
const open = Boolean(anchorEl);
}
childComponent.propTypes = {
boxStyle: PropTypes.object
};
You are destructuring on that line, if you want to set default value, you need to use = char and not :
const { boxStyle = PropTypes.object} = props;
And yes, this is not the way to set propTypes. Here's example from docs:
Class based:
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
Function based:
import PropTypes from 'prop-types'
function HelloWorldComponent({ name }) {
return (
<div>Hello, {name}</div>
)
}
HelloWorldComponent.propTypes = {
name: PropTypes.string
}
export default HelloWorldComponent
Sometimes when setting up props we think that prop is only of type object. But due to a lot of factors (for example, how we fetch data and set props) a prop can be undefined for an initial period of time (very short) and then be defined as an object once the data is loaded.
To avoid you can either:
Change the way the data is supplied and therefore make sure that the prop is always an object
Allow in propTypes for both values: undefined (for initial moment) and then object as well once it loads. Here's how to specify multiple propTypes (without the use of default props):
// An object that could be one of many types
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),

Programatically create and mount components with vue 3

I need to programatically create and mount components on the fly from parent component.
It works fine with Vue 2.
import Vue from 'vue'
// ParentComponent.vue
export default {
methods: {
createChild() {
const Child = Vue.extend(ChildComponent)
const child = new Child({
propsData,
store: this.$store,
i18n: this.$i18n
}).$mount()
}
}
}
But I cannot figure out how-to do with Vue 3.
I have finally ended with this, after finding some info here:
https://github.com/vuejs/vue-next/issues/1802
https://github.com/pearofducks/mount-vue-component
// ParentComponent.vue
import { h, render } from 'vue'
export default {
methods: {
createChild() {
const child = h(ChildComponent, propsData)
child.appContext = this.$.appContext // use store, i18n
const el = document.createElement('div')
render(node, el)
}
}
}

How to use Hooks inside useEffect?

I wrote a demo here:
import React, { useRef, useEffect, useState } from "react";
import "./style.css";
export default function App() {
// let arrRef = [useRef(), useRef()];
let _data = [
{
title: A,
ref: null
},
{
title: B,
ref: null
}
];
const [data, setData] = useState(null);
useEffect(() => {
getDataFromServer();
}, []);
const getDataFromServer = () => {
//assume we get data from server
let dataFromServer = _data;
dataFromServer.forEach((e, i) => {
e.ref = useRef(null)
});
};
return (
<div>
{
//will trigger some function in child component by ref
data.map((e)=>(<div title={e.title} ref={e.ref}/>))
}
</div>
);
}
I need to preprocess after I got some data from server, to give them a ref property. the error says 'Hooks can only be called inside of the body of a function component' . so I checked the document, it says I can't use hooks inside a handle or useEffect. so is there a way to achieve what I need?
update:
I need to create component base on DB data, so when I create a component I need to give them a ref , I need trigger some function written in child component from their parent component and I use ref to achieve that. that is why I need to pass a ref to child component.

Is it possible to pass a string array as parameter using HOC?

I'm using some HOC components in my nextJS application to set some prop values via getInitialProps.
But I need to use dynamic values for these props.
In my index component I'm calling withServerProps. Is it possible to pass some string array to it?
index.js
import React, { Component } from 'react'
import { withTranslation } from 'i18n'
import withServerProps from 'with-server-props'
class Index extends Component {
render () {
return (<div>Some content</div>)
}
}
export default withServerProps( // <-- How to pass an array with strings?
withTranslation()(Index)
)
I need to get the string array in this function:
with-server-props.js
import React, { Component } from 'react'
export default WrappedComponent =>
class extends Component {
static async getInitialProps (context) {
const { query } = context
return {
id: query && query.id
target: PASSED_ARRAY // <-- Need to recieve the array here
}
}
render () {
return <WrappedComponent {...this.props} />
}
}
Yes you definitely can. Just add some arguments during the export in index.js.
export default withServerProps(withTranslation()(Index), ["hello"])
Then in your HOC:
export default function handleServerProps(WrappedComponent, arr) {
class Hoc extends Component {
static async getInitialProps (context) {
const { query } = context
return {
id: query && query.id,
target: arr,
}
}
render () {
return <WrappedComponent {...this.props} />
}
}
return Hoc;
}

Categories