javascript - helmet - Nombre de componente dinámico React/JSX
react helmet ssr (9)
Debe haber un contenedor que asigne los nombres de los componentes a todos los componentes que se supone que deben usarse dinámicamente.
Las clases de componentes deben registrarse en un contenedor porque, en un entorno modular, no hay un lugar único donde se pueda acceder.
Las clases de componentes no se pueden identificar por sus nombres sin especificarlas explícitamente porque el
name
función se minimiza en la producción.
Mapa de componentes
Puede ser objeto simple:
class Foo extends React.Component { ... }
...
const componentsMap = { Foo, Bar };
...
const componentName = ''Fo'' + ''o'';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
O instancia de
Map
:
const componentsMap = new Map([[Foo, Foo], [Bar, Bar]]);
...
const DynamicComponent = componentsMap.get(componentName);
El objeto simple es más adecuado porque se beneficia de la taquigrafía de la propiedad.
Módulo de barril
Un módulo de barril con exportaciones con nombre puede actuar como tal mapa:
// Foo.js
export class Foo extends React.Component { ... }
// dynamic-components.js
export * from ''./Foo'';
export * from ''./Bar'';
// some module that uses dynamic component
import * as componentsMap from ''./dynamic-components'';
const componentName = ''Fo'' + ''o'';
const DynamicComponent = componentsMap[componentName];
<DynamicComponent/>;
Esto funciona bien con una clase por estilo de código de módulo.
Decorador
Los decoradores se pueden usar con componentes de clase para azúcar sintáctico, esto aún requiere especificar los nombres de clase explícitamente y registrarlos en un mapa:
const componentsMap = {};
function dynamic(Component) {
if (!Component.displayName)
throw new Error(''no name'');
componentsMap[Component.displayName] = Component;
return Component;
}
...
@dynamic
class Foo extends React.Component {
static displayName = ''Foo''
...
}
Se puede usar un decorador como componente de orden superior con componentes funcionales:
const Bar = props => ...;
Bar.displayName = ''Bar'';
export default dynamic(Bar);
El uso de la característica no estándar en lugar de la propiedad aleatoria también beneficia la depuración.
Estoy tratando de representar dinámicamente componentes en función de su tipo.
Por ejemplo:
var type = "Example";
var ComponentName = type + "Component";
return <ComponentName />;
// Returns <examplecomponent /> instead of <ExampleComponent />
Probé la solución propuesta aquí React / JSX nombres de componentes dinámicos
Eso me dio un error al compilar (usando browserify para tragar). Esperaba XML donde estaba usando una sintaxis de matriz.
Podría resolver esto creando un método para cada componente:
newExampleComponent() {
return <ExampleComponent />;
}
newComponent(type) {
return this["new" + type + "Component"]();
}
Pero eso significaría un nuevo método para cada componente que creo. Debe haber una solución más elegante para este problema.
Estoy muy abierto a sugerencias.
Descubrí una nueva solución. Tenga en cuenta que estoy usando módulos ES6, así que estoy requiriendo la clase. También podría definir una nueva clase React en su lugar.
var components = {
example: React.createFactory( require(''./ExampleComponent'') )
};
var type = "example";
newComponent() {
return components[type]({ attribute: "value" });
}
Editar: Otras respuestas son mejores, ver comentarios.
Resolví el mismo problema de esta manera:
...
render : function () {
var componentToRender = ''component1Name'';
var componentLookup = {
component1Name : (<Component1 />),
component2Name : (<Component2 />),
...
};
return (<div>
{componentLookup[componentToRender]}
</div>);
}
...
Hay una documentación oficial sobre cómo manejar tales situaciones disponible aquí: https://facebook.github.io/react/docs/jsx-in-depth.html#choosing-the-type-at-runtime
Básicamente dice:
Incorrecto:
import React from ''react'';
import { PhotoStory, VideoStory } from ''./stories'';
const components = {
photo: PhotoStory,
video: VideoStory
};
function Story(props) {
// Wrong! JSX type can''t be an expression.
return <components[props.storyType] story={props.story} />;
}
Correcto:
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} />;
}
Para un componente contenedor, una solución simple sería usar
React.createElement
directamente (usando ES6).
import RaisedButton from ''mui/RaisedButton''
import FlatButton from ''mui/FlatButton''
import IconButton from ''mui/IconButton''
class Button extends React.Component {
render() {
const { type, ...props } = this.props
let button = null
switch (type) {
case ''flat'': button = FlatButton
break
case ''icon'': button = IconButton
break
default: button = RaisedButton
break
}
return (
React.createElement(button, { ...props, disableTouchRipple: true, disableFocusRipple: true })
)
}
}
Si sus componentes son globales, simplemente puede hacer:
var nameOfComponent = "SomeComponent";
React.createElement(window[nameOfComponent], {});
Supongamos que deseamos acceder a varias vistas con carga dinámica de componentes. El siguiente código ofrece un ejemplo práctico de cómo lograr esto mediante el uso de una cadena analizada a partir de la cadena de búsqueda de una url.
Supongamos que queremos acceder a una página ''snozberrys'' con dos vistas únicas utilizando estas rutas de URL:
''http://localhost:3000/snozberrys?aComponent''
y
''http://localhost:3000/snozberrys?bComponent''
Definimos el controlador de nuestra vista de esta manera:
import React, { Component } from ''react'';
import ReactDOM from ''react-dom''
import {
BrowserRouter as Router,
Route
} from ''react-router-dom''
import AComponent from ''./AComponent.js'';
import CoBComponent sole from ''./BComponent.js'';
const views = {
aComponent: <AComponent />,
console: <BComponent />
}
const View = (props) => {
let name = props.location.search.substr(1);
let view = views[name];
if(view == null) throw "View ''" + name + "'' is undefined";
return view;
}
class ViewManager extends Component {
render() {
return (
<Router>
<div>
<Route path=''/'' component={View}/>
</div>
</Router>
);
}
}
export default ViewManager
ReactDOM.render(<ViewManager />, document.getElementById(''root''));
Utilicé un enfoque un poco diferente, ya que siempre conocemos nuestros componentes reales, así que pensé en aplicar la caja del interruptor. También el número total de componentes fue de alrededor de 7-8 en mi caso.
getSubComponent(name) {
let customProps = {
"prop1" :"",
"prop2":"",
"prop3":"",
"prop4":""
}
switch (name) {
case "Component1": return <Component1 {...this.props} {...customProps} />
case "Component2": return <Component2 {...this.props} {...customProps} />
case "component3": return <component3 {...this.props} {...customProps} />
}
}
<MyComponent />
compila a
React.createElement(MyComponent, {})
, que espera una cadena (etiqueta HTML) o una función (ReactClass) como primer parámetro.
Simplemente puede almacenar su clase de componente en una variable con un nombre que comienza con una letra mayúscula. Ver etiquetas HTML vs React Components .
var MyComponent = Components[type + "Component"];
return <MyComponent />;
compila a
var MyComponent = Components[type + "Component"];
return React.createElement(MyComponent, {});