How to dynamically request a React Component based on a variable value? - javascript

I'm wondering how to dynamically request a component based on a variable value. What I'm trying to accomplish here is the following:
import Template1 from './Template1.jsx';
import Template2 from './Template2.jsx';
var ComponentTemplate = (some_condition === true) ? "Template1" : "Template2"
render() {
<ComponentTemplate prop1="prop1 val" />
}
Is this even possible? If so, how?

It is not clear to me why you need to use a string representation of a class rather than just switch the component or use a conditional render:
var Component = some_condition === true ? Template1 : Template2;
// ...
return ( <Component /> );
But assuming this is an oversimplification, the easiest thing to do would be to use a mapping object that translates a string into a component. With ES2015 enhanced object literals, it becomes fairly straightforward:
var Components = {
Template1,
Template2,
};
var Component = condition ? Components['Template1'] : Components['Template2'];
// ...
return ( <Component /> );

If you are just looking to render different component based on the condition you could actually have 2 other render function and inside render() you could check the condition and call corresponding render
render () {
!this.state.isAuthorized? renderLogin(): renderTweets();
}
renderLogin () {
<LoginView/>
}
renderTweet () {
<ListTweets/>
}
Hope this is what you were looking for!

All the dynamic rendering should inside Render function. Because the JSX compile will depend on the Variable name not the Object reference.
If you write <Component />,
it will transfer to React.createElement('Component').
Cannot refer to the dynamic component you want to choose.
depend on the condition when Render is running. Use different React tag you want.
render() {
condition ? <Template1 /> : <Template2 />
}

Related

Pass props to React component stored in a variable extracted out of props.children

I want to insert some props to a React component which I have extracted out of props.children, like so:
<PageContainer>
<PageOne />
<PageTwo />
<PageThree />
</PageContainer>
Inside <PageContainer> i am extracting the current page via props.children and current page index, something like this:
const { children, pageIndex } = props;
let activePage = React.Children.toArray(children)[pageIndex];
Inside this PageContainer I have the "oportunity" to send down a function that I need inside the <PageOne>, <PageTwo> and <PageThree>. I tried something like this, but then I got some compiling problems, I think. Since this worked locally and not in my test environment:
const newProps = React.cloneElement(activePage.props.children, { myWantedFunction: myWantedFunctionThatIsAvailableInsidePageContainer });
activePage = React.cloneElement(activePage, { children: newProps });
The problem here is that myWantedFunction is not working in the test environment, but it is working locally. It says that it is not a function in the test environment, but when I console.log it out locally, it prints out a function. I am guessing there is a compiling problem, but I am wondering if there is a better way to acheive this (send down props to a component stored in a variable that I got out of props.children)?
Thank you in advance!
You are almost correct, you need to use React.cloneElement() to send props down to the child component. First you need to get the active page which you're doing right:
let activePage = React.Children.toArray(children)[pageIndex];
Then you need to use React.cloneElement to pass props to this component like this. Let's the name of the prop is isAuthenticated which is a boolean:
let component = React.cloneElement(activePage, { isAuthenticated: true })
Now in your page you'll be able to access this prop: prop.isAuthenticated
I created a working demo for you, have a look:
https://codesandbox.io/s/determined-kowalevski-eh0ic?file=/src/App.js
Hope this helps :)
Alternatively you could also do something like this:
const PAGES = [PageOne, PageTwo, PageThree]
function PageContainer(props){
const {pageIndex} = props
const ActivePage = PAGES[pageIndex]
return <ActivePage someFunction={someFunction} />
function someFunction(){
// the function you want to pass to the active page
}
}
Node that ActivePage has a capital A, which allows it to be used as a JSX component.
A drawback of this solution is however that if you use typescript it wont type check the props correctly since you only know which component is to be rendered at runtime. You could replace the array lookup with a switch to avoid that issue.
Yet another variation would be to just let the Page components handle their own state and pass the function to all of them. Like this:
function PageContainer(props){
const {pageIndex} = props
const someFn = () => 0
return <React.Fragment>
<PageOne id={1} activePage={pageIndex} someFunction={someFn} />
<PageTwo id={2} activePage={pageIndex} someFunction={someFn} />
<PageThree id={3} activePage={pageIndex} someFunction={someFn} />
</React.Fragment>
}
then in the Page Components just check if the pageIndex corresponds to their id:
function PageOne(props){
const {id, activePage, someFunction} = props
if (id === activePage){
const result = someFunction()
return <div>Page One: {result}</div>
}
}

How to read child elements from a React component

I want to get a list of all child elements of a react component.
For example:
Parent.js
<Parent>
<SomeComponent/>
</Parent>
SomeComponent.js
<SomeComponent>
<ChildElement1/>
<ClhidElement2/>
</SomeComponent>
So in Parent.js I want to get ChildElement1 and ChildElement2.
Is that possible?
My Use case is:
I'm passing form fields (Field component) to a generic Form component. The Form element receives an object of default values and also other things (what CRUD/resource it is related to for example). And it must inject these values in the Fields. Instead of passing all fields one by one and avoid repetion I created containers like "UserFields" and a few others and they are the ones that have the Fields components. So I need Form to read the Fields in UserFields. But since the fields are already inside UserFields, I can't figure out how to get them.
React is designed to be unidirectional data flow and following Flux architecture, and hence to keep best practices, it's always top down (from parent to child, not bidirectional).
However, you can achieve them in several options such as implementing React with redux or React Context
I am considering that your child components are mapped from array inside <SomeComponent />
Try this inside your parent
state = {
child: []
}
renderChildren = () => {
if(this.state.child.length > 0) {
return this.state.child.map(e => {
return (
<div>{e}</div>
)
})
}
}
returnChild = (data) => {
var child = [];
for(var i = 0; i < data.length; i++) {
child.push(data[i])
}
this.setState(prevState => ({child: [...prevState.child, child]}));
}
return (
<div>
<SomeComponent returnChild={(child) => this.returnChild(child)} />
{this.renderChildren()}
</div>
)
Add this method to your <SomeComponent /> component like this along with other code.
onGettingMoreChild = (child) => {
this.props.returnChild(child)
}
Don't forget to call onGettingMoreChild whenever there is a new child created.
I have not tested this code. Please playaround with it if needed. Also, remember to pass in your entire view as child to the method onGettingMoreChild
Example of child variable passed to onGettingMoreChild is
<div>I am child one!!</div>

Ternary inside of a mapping function using ReactJS

I'm using React to display a Material Design Grid List of news articles. Im passing the JSON I receive to this GridList component (this.props.newsArticles), and mapping through each returned result so I can filter the data based on whether it has a hi-def image, then sending the data to MediaTile to get rendered. I'm having a problem with my ternary in GridList module's hasHiDefPictures function. I receive a syntax error.
const Components = {
MediaTile: React.createClass({
render: function() {
return (
<GridTile
title={this.props.tile.title}
>
<img src={this.props.tile.multimedia[0].url} />
</GridTile>
)
}
}),
GridList: React.createClass({
render: function() {
var newsArticles = this.props.newsArticles
var renderArticles = newsArticles.data.results.map(function(tile, key) {
return hasHiDefPictures(tile, key)
})
function hasHiDefPictures(tile, key) {
return {tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
};
return (
<div>
<GridList>
{renderArticles}
</GridList>
</div>
);
}
})
}
Now the only way to get around this syntax error is to wrap that returned value in div's like so:
function hasHiDefPictures(tile, key) {
return (
<div>
{tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
</div>
)
};
But I do not want to do that. Does anyone have advice on how to get around this problem? I'm still new to react, so there's a good chance that I'm just not handling the data in the proper place. Any help would be appreciated! Thanks!
You just need to remove the {} around your ternary. {} is useful in JSX (an expression starting with <) to evaluate some JS code, but your array mapping already occur in pure JS code (the beginning of the render function) so it has the regular JS meaning: a block or an object literal.

Dynamically choosing tag in React

I have some React components that use SVG, and can be contained within an SVG or not. Consequently, I'd like to have a simple way of letting them render inside svg or g, depending on a param, something like:
export default class SVGComponent extends React.Component{
get containerElement() {
return this.props.inSVG ? 'g' : 'svg';
}
render() {
return(<{containerElement}>
<text>Extendable component</text>
</{containerElement}>);
}
}
This doesn't work: you can't use template injection in JSX this way. Is there another way I could approach it? It seems like I might be able to do React.DOM[containerElement], but I gather React does not like mixing React.DOM and JSX, and I don't want to move entirely over to React.DOM syntax just for this one function.
Take a look at this answer Dynamic tag name in jsx and React
It seems you can create a variable with the first letter capitalized and use it like a component like this
const CustomTag = `h${this.props.priority}`;
<CustomTag>Hello</CustomTag>
If this.props.priority == 3 this will render <h3>Hello</h3>
You can do it like this
render(){
var content = <text>Extendable component</text>;
if(this.props.inSVG){
return (
<g>{content}</g>
);
}
else {
return (
<svg>{content}</svgg>
);
}
}
I would probably do something like this:
export default class SVGComponent extends React.Component{
get containerElement() {
var content = (<text>Extendable component</text>);
return this.props.inSVG ? (<g>{content}</g>) : (<svg>{content}</svg>);
}
render() {
return({containerElement()});
}
}
But, it really depends on how dynamic the <text> is.
If the child elements are very dynamic, you might want to render this.props.children and add then as children of the component.

What are appropriate return types for a ReactJS Component.render() function?

The React Tutorial has a render apparently returning an array of rendered nodes:
// tutorial10.js
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
What are acceptable return types for a render() method for a component? An array seems to work? What about an array of arrays? If I have data stored in an array of arrays of codes, how do I appropriately render the grid?
You must always return a react component, but the contents of that component (as long as they are valid jsx syntax) can be an array, an array of arrays containing react components. E.g.
var MyComponent = React.createClass({
render: function () {
var single = <p>Single</p>;
var array = [
<p>One</p>,
[<p>Two</p>],
[<p>Three</p>]
];
return (
<div>
{single}
{array}
</div>
);
}
});
React.render(<MyComponent />, document.body);
jsbin
Typically you would take the data, iterate over it (however many levels deep) and generate your components. Array.Map and other "functional" helpers are particularly helpful in situations like this.
If you check the docs you'll see that you can only return a component or null/false from render.
"The render() method is required. When called, it should examine this.props and this.state and return a
single child component. This child component can be either a virtual
representation of a native DOM component (such as or
React.DOM.div()) or another composite component that you've defined
yourself.
You can also return null or false to indicate that you don't want
anything rendered. Behind the scenes, React renders a tag
to work with our current diffing algorithm. When returning null or
false, this.getDOMNode() will return null."
http://facebook.github.io/react/docs/component-specs.html

Categories