Which is better practice??
{someBoolean ? <showMe/> : null }
or
{someBoolean && (<showMe/>)}
does it matter in the end? what are pros and cons? do we need to call null? Can one vs the other cause problems down the road? what is different syntax doing anyway?
On my case I prefer using this {someBoolean && ()} instead of the {someBoolean ? : null }. This answer is based on my preference.
Pros: you will never need to consider the ':null' else statement on the end and if you have a lot of types for the someBoolean for example you have default props for that component and your report form will have a lot of someBooleantypes you need to
consider them at the same time.
In cases like
<ParentComponent />
<ChildComponent someBoolean={true}/>
</ParentComponent>
<ParentComponent />
<ChildComponent someBooleanv2={true}/>
</ParentComponent>
ChildComopnent.jsx
const ChildComponent =({someBoolean,someBooleanv2})=>{
return (<div>
{someBoolean && !someBooleanv2 (<showMe/>)}
{someBooleanv2 && !someBoolean (<showMeV2/>)}
<div/>);
}
Most of the time I use it on a single ReportsForm if I would like to use it overtime to be efficient with formik functionalities
Related
I would like to refactor some legacy code. The use case is this. Someone previously had created a new component that basically had all the exact same attributes as the old component. The render method returns either old or new based on a flag, but the attribute values are all the same. This results in many redundant lines that were copy pasted.
return (
isSomeBoolTrue ? (<OriginalComponent a={a} b={b} ..... z={z} />):
(<NewComponent a={a} b={b} ..... z={z} />)
);
I'd like some way to remove the duplicate attributes and have it look something like this:
return (isSomeBoolTrue ? (<OriginalComponent):(<NewComponent) a={a} b={b} .... />
You can also try the following approach.
const SelectedComponent = isSomeBoolTrue ? OriginalComponent : NewComponent;
return <SelectedComponent a={a} b={b} z={z} />
One option is to put the props into an object, then spread the object into the each component's props while using the conditional operator.
const propsToPassDown = { a, b, z };
return (
isSomeBoolTrue
? <OriginalComponent {...{ propsToPassDown }} />
: <NewComponent {...{ propsToPassDown }} />
);
Another approach is to use React.createElement instead of JSX, allowing you to use the conditional operator when selecting the component to pass as the first argument.
return React.createElement(
isSomeBoolTrue ? OriginalComponent : NewComponent,
{ a, b, z },
null
);
I am currently working on a project that requires dynamically injecting one component into another.
My project is using Redux, so I came up with two possible solutions which both have their advantages and disadvantages, but I don't know which one to choose. I know that by nature, React encourages composition, but I'm still curious to know if the second approach (simpler and faster to use) is still good :
export const SlideOverComponents = {
'UserCreate': UserCreate,
'UserUpdate': UserUpdate,
};
The idea is to register all components that can be injected as a key value pair, and dispatch a Redux action with the key and the props required by this component.
{(!!componentKey && !!SlideOverComponents[componentKey]) && React.createElement(SlideOverComponents[componentKey], props)}
Then in my parent container, I just read this key and use the React.createElement to display the injected one.
This solution is working fine and is easy and fast to use because I just have to register any new component to the object to make it work.
Is this approach "ok" ? Or should I use composition ?
(I'm asking from a "good practice" or "anti-pattern" point of view.)
Yes that's fine, as long as the interface between all of the SlideOverComponents are completely identical. Your code is more verbose than it needs to be. You don't need createElement either if you assign it to a variable first
const Component = SlideOverComponents[componentKey]
return (
<div>
{Component && <Component {...props} />}
</div>
)
Edit:
I noticed that you are using TypeScript from other answers. Considering that, I still think you can use Composition but with types using String Literal Types like this:
type SlideOverComponentsType = "update" | "create";
type SlideOverComponentsProps = UserUpdateProps | UserCreateProps;
type SlideOverProps = {
key: SlideOverComponentsType;
} & SlideOverComponentsProps;
function SlideOver({ key, ...props }: SlideOverProps) {
switch (key) {
case "update":
return <UserUpdate {...props} />;
case "create":
return <UserCreate {...props} />;
default:
return null; // this will never happen but need to be addressed
}
}
And with an approach like that, you don't need an "Object" to store all the possible types of SlideOverComponents. You also guarantee that the props will always be using the proper interface and if eventually, you pass it wrongly TS will warn you about that.
Again: consider using types instead of declaring "options" as objects for cases like this.
Hope that this could help you or give you some good ideas!
Original Answer:
You can still use Composition for this and create some kind of check or `switch` statement inside the "Generic" Component. That way you could avoid adding so many checks(`if`s) outside of the parent component and guarantee that eventually non-existing `keys` could fallback to a default behavior or even to an error.
There are several ways of implementing it but one using switch that I like is this one:
function UserInteraction({ key, ...props }) {
switch (key) {
case "create": {
return <UserCreate {...props} />;
}
case "update": {
return <UserUpdate {...props} />;
}
default: {
return null;
// or you could thrown an error with something like
throw new Error(`Error: key ${key} not present inside component User`);
}
}
}
You could also use the Object.keys() method to accomplish almost the same behavior:
const UserInteractionOptions = {
"create": UserCreate,
"update": UserUpdate,
}
function UserInteraction({ key, ...props }) {
if (!Object.keys(UserInteractionOptions).includes(key)) {
return null;
// or you could thrown an error with something like
throw new Error(`Error: key ${key} not present inside component User`);
}
const InteractionComponent = UserInteractionOptions[key];
return <InteractionComponent {...props} />;
}
The main idea is to isolate the logic from deciding which component to render (and if it can be rendered) inside that component.
For future reading, you could check on TypeScript and how this can be easily handled by types, coercion, and the checks for non-present keys could be made before even the code runs locally.
A little of nitpicking: you are not "injecting" a Component inside another Component. You are just passing a key to deciding if the Parent Component renders or not the Child component through a flag. The injection of one Component into another involves passing the full component as a prop and just rendering it (or customizing it, eventually).
You could look at how React decides to render the children prop and how it decides if it is null, a string, or a ReactComponent to render an actual component. Also, a good topic to research is Dependency Injection.
As a simple example, injecting a component could looks like this:
function Label({ text }) {
return <p>{text}</p>;
}
function Input({ Label, ...props }) {
return (
<div>
<Label />
<input {...props} />
</div>
);
}
Short version: I have a component type (a class or a function) and props for it. I need to "render" the component to obtain its representation in JSX elements.
(I use the quotes because I mean «render into JSX elements» not «render into UI» and I am not sure about the terminology.)
Example:
const Foo = (props) => <div><Bar>{props.x + props.y}</Bar></div>;
// is an equivalent of `const elements = <div><Bar>3</Bar></div>;`
const elements = render2elements(Foo, { x: 1, y: 2 });
function render2elements(type, props) {
/* what should be here? */
}
Long version (for background story enthusiasts, may be skipped imo)
I have a React code whose very simplified version looks like this:
function Baby(props) {
/* In fact, it does not even matter what the component renders. */
/* It is used primarily as a configuration carrier. */
}
function Mother({ children }) {
const babies = getAllBabies(React.Children.toArray(children));
const data = parseData(babies);
return buildView(data);
}
function SomeOtherComponent(props) {
const { someProps1, someProps2,
someProps3, someCondition } = someLogic(props);
return (
<Mother>
<Baby {...someProps1} />
<Baby {...someProps2} />
{someCondition ? <Baby {...someProps3} /> : null}
</Mother>
);
}
It may be strange but it works. :) Until someone wants to do a little refactoring:
function Stepmother(props) {
const { someProps1, someProps2,
someProps3, someCondition } = someLogic(props);
return (
<>
<Baby {...someProps1} />
<Baby {...someProps2} />
{someCondition ? <Baby {...someProps3} /> : null}
</>
);
}
function SomeOtherComponent(props) {
return <Mother><Stepmother {...props} /></Mother>;
}
Now the Mother receives in its children only a JSX element for the Stepmother and can not parse the JSX elements for the Baby'ies. :(
So we return to my original question: I need to "render" Stepmother and then parse its internal JSX representation. But how can I do this?
P.S. I used functional components for brevity, but of course, all examples could use class components as well.
Thank you.
Don't do that.
I strongly encourage you to just rethink this solution altogether, ESPECIALLY if
It is used primarily as a configuration carrier.
...but.
So this kinda works however there's a couple of caveats:
if a component passed to that function is a class component and has some state, you won't be able to use any of it, in general it will probably cause a ton of issues that I'm not aware of
if a component passed is a function component, you can't use any hooks. It will just throw an error at you.
function render2elements(component, props) {
if (component.prototype.isReactComponent) {
return new component(props).render();
}
return component(props);
}
So if your "babies" are really simple this technically would work. But you just shouldn't refactor it the way you want and, again, ideally rethink this whole concept.
I am trying to use the component "react-sortable-how". I wrote this:
<SortableContainer>
{ questions.map((question,index) => (
<SortableElement key={index}>
<QuestionForm key={index} question={question} mode="update" betId={this.props.match.params.betId} cb={this.getQuestions} />
</SortableElement>
)) }
</SortableContainer>
But I have this error :
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
I am a beginner in React. I don't understand what this message mean. I tried a lot of things without success.
I'm trying to load in a view a component which is: <Loader/>
The thing is that the component is structured like this:
<Loader.BoxTitleContainer>
<Loader.MainTitle>
<Loader.Title>{`Loading...`}</Loader.Title>
</Loader.MainTitle>
</Loader.BoxTitleContainer>
<Loader.Container className={'loader'}>
<Loader textDisabled={true} />
</Loader.Container>
What I actually want to do is to load <Loader/> the way it is but preventing it to load <Loader.BoxTitleContainer> without altering the structure (which is on top of this paragraph). How can I do it? Any solid example?
Thanks!
You would need to add a boolean prop to your Loader component which would render BoxTitleContainer if set to true, and vice versa, like this:
const { showBoxTitleContainer } = this.props;
return (
{showBoxTitleContainer && <Loader.BoxTitleContainer>
<Loader.MainTitle>
<Loader.Title>{`Loading...`}</Loader.Title>
</Loader.MainTitle>
</Loader.BoxTitleContainer>}
<Loader.Container className={'loader'}>
<Loader textDisabled={true} />
</Loader.Container>
);
The alternative would be to divide the structure in two React components:
// BoxTitleContainer
<Loader.BoxTitleContainer>
<Loader.MainTitle>
<Loader.Title>{`Loading...`}</Loader.Title>
</Loader.MainTitle>
</Loader.BoxTitleContainer>
// LoaderContainer
<Loader.Container className={'loader'}>
<Loader textDisabled={true} />
</Loader.Container>
You can't accomplish this without altering the structure.
if Loader.BoxTitleContainer is your's component, you can introduce a property, let say, hide, or if that is 3dparty component see it's possible properties (sometimes it is better just to see the source code, as docs sometims missing some details). Inside the BoxTitleContainer there should be a check for value of hide for the decision on either render the component or not.
In this approach you can easily control your title like (see hide={this.props.hideTitle || false} as a property for BoxTitle):
<Loader.BoxTitleContainer hide={this.props.hideTitle || false}>
<Loader.MainTitle>
<Loader.Title>{`Loading...`}</Loader.Title>
</Loader.MainTitle>
</Loader.BoxTitleContainer>
<Loader.Container className={'loader'}>
<Loader textDisabled={true} />
</Loader.Container>
and then in your <Loader /> you can do <Loader hideTitle={true}/>
If the component is not yours and it doesn't have any similar to hide property, then you can do HOC component to wrap this BoxTitle one and then introduce the hide propery in it.