How can i make component accepts elements inside? - javascript

I want to make a component but i want that one accepts elements too. For example :
Component :
const Example = () => {
return (
<div>
//Some Elements will come here.
</div>
)
}
Another Page :
const App = () => {
return (
<Example>
<div>
<h1>Hello all </h1>
<p>I want that elements acceptable on my custom component </p>
</div>
</Example>
)
}
But i only can send props and i cant write anything inside of tags of my component. How can i make it ? Thanks for all!

React defined a special prop called children. That's what you exacly need.
Try like this
const Example = ({ children }) => {
return <div>{children}</div>;
};
const App = () => {
return (
<Example>
<div>
<h1>Hello all </h1>
<p>I want that elements acceptable on my custom component </p>
</div>
</Example>
);
};

You can use props.children
const Example = props => {
return <div>{props.children}</div>;
};

Related

Rendering Child Component from store

I have a component which has child components, i want to render these child components with different Ids. They are getting their data from store.The problem is they are rendered but with the same item. how can this be solved?
MultiImages Component
const MultiImages: () => JSX.Element = () => {
const values = ['500', '406', '614'];
return (
<div>
{values.map((val, index) => {
return <OneImage key={index} projectID={val} />;
})}
</div>
);
};
export default MultiImages;
OneImage Component
const OneImage: () => JSX.Element = ({ projectID }) => {
const projectData = useProjectDataStore();
const { getProject } = useAction();
useEffect(() => {
getProject(projectID ?? '');
}, []);
return (
<>
<div>
<img
src={projectData.picture}
}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
);
};
export default OneImage;
Your issue here - you are calling in a loop, one by one fetch your projects, and each call, as far as we can understand from your example and comments override each other.
Your are doing it implicitly, cause your fetching functionality is inside your Item Component OneImage
In general, the way you are using global state and trying to isolate one from another nodes is nice, you need to think about your selector hook.
I suggest you, to prevent rewriting too many parts of the code, to change a bit your selector "useProjectDataStore" and make it depended on "projectID".
Each load of next project with getProject might store into your global state result, but instead of overriding ALL the state object, you might want to use Map(Dictionary) as a data structure, and write a result there and use projectID as a key.
So, in your code the only place what might be change is OneImage component
const OneImage: () => JSX.Element = ({ projectID }) => {
// making your hook depended on **projectID**
const projectData = useProjectDataStore(projectID);
const { getProject } = useAction();
useEffect(() => {
// No need of usage **projectID** cause it will inherit if from useProjectDataStore
getProject();
}, []);
return (
<>
<div>
<img
src={projectData.picture}
}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
);
};
export default OneImage;
And inside of your useProjectDataStore store result into a specific key using projectID.
Your component OneImage will return what's in the return statement, in your case:
<>
<div>
<img
src={projectData.picture}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
</>
This tag <></> around your element is a React.fragment and has no key. This is the reason you get this error.
Since you already have a div tag wrapping your element you can do this:
<div key={parseInt(projectID)}>
<img
src={projectData.picture}
/>
<div>
<a>
{projectData.projectName}
</a>
</div>
</div>
You can also change the key to Math.floor(Math.random() * 9999).
Note that passing the prop key={index} is unnecessary, and is not advised to use index as keys in a react list.

What is the React way of inserting an icon into another component?

I'm trying to create an WithIcon wrapper component which would insert a child (icon) into a wrapped component.
Let's say I have a button:
<Button>Add item</Button>
I want to create a component WithIcon which will be used like this:
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
Ultimately what I want to achieve is this:
<Button className="with-icon"><i className="me-2 bi bi-{icon}"></i>Add item</Button>
Notice the added className and the tag within the Button's body.
I'm trying to figure out how the WithIcon component's code should look like. What is the React way of achieving this result?
The hardest part was the rules of using the WithIcon Will we only have one ?
Will we have only it at the leftmost ? Something like that.
But if we follow your example. We can relatively write something like this for the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
return (
<>
<i className={`me-2 bi bi-${i}`}></i>
{React.cloneElement(child, { className: "with-icon" })}
</>
);
});
};
Then we can just use it the way you want it
<WithIcon i="plus"><Button>Add item</Button></WithIcon>
What we do is just looping through the children which in react is any nested jsx you throw in it (Button in our case)
You can find my fiddle here : https://codesandbox.io/s/react-font-awesome-forked-321tz?file=/src/index.js
UPDATE
So my previous answer does not fully meet the end result we want. The will need to be the main parent
The idea is still quite the same as before but here we are infering the type of the component we passed inside the WithIcon This also adds a safeguard when we passed a nested component inside the WithIcon
const WithIcon = ({ i, children }) => {
return React.Children.map(children, (child) => {
const MyType = child.type; // So we can get the Button
return (
<MyType className="with-icon">
<i className={`me-2 bi bi-${i}`}></i>
{(React.cloneElement(child, {}), [child.props.children])}
</MyType>
);
});
};
I think I'll go to sleep I'll update the rest of the explanation at later date.
See the fiddle here :
https://codesandbox.io/s/react-font-awesome-forked-y43fx?file=/src/components/WithIcon.js
Note that this code does not preserved the other props of the passed component, but you can relatively add that by adding {...child.props} at the MyComponent which is just (reflection like?) of infering the component.
Of course also have another option like HOC Enhancers to do this but that adds a bit of complexity to your how to declare your component api. So Pick whats best for ya buddy
Maybe try using a higher order component?
const withIcon = (icon, Component) => ({children, ...props}) => {
return (
<Component className="with-icon" {...props}>
<i className=`me-2 bi bi-${icon}` />
{children}
</Component>
);
}
Then the usage is
const ButtonWithIcon = withIcon("your-icon", Button);
<ButtonWithIcon>Add Item</ButtonWithIcon>
From my experience with react it usually comes down to either using a property inside the component like here (https://material-ui.com/api/button/) or higher order component like what I described.
There are two common patterns used in React for achieving this kind of composition:
Higher-Order Components
Start by defining a component for your button:
const Button = ({ className, children }) => (
<button className={className}>{children}</button>
);
Then the higher-order component can be implemented like this:
const withIcon = (Component) => ({ i, className = '', children, ...props }) => (
<Component {...props} className={`${className} with-icon`}>
<i className={`me-2 bi bi-${i}`} />
{children}
</Component>
);
Usage:
const ButtonWithIcon = withIcon(Button);
<ButtonWithIcon i="plus">Add Item</ButtonWithIcon>
Context
Start by defining the context provider for the icon:
import { createContext } from 'react';
const Icon = createContext('');
const IconProvider = ({ i, children }) => (
<Icon.Provider value={i}>{children}</Icon.Provider>
);
and then your component:
import { useContext } from 'react';
const Button = ({ className = '', children }) => {
const i = useContext(Icon);
if (i) {
className += ' with-icon';
children = (
<>
<i className={`me-2 bi bi-${i}`} />
{children}
</>
);
}
return (
<button className={className}>{children}</button>
);
};
Usage:
<IconProvider i="plus"><Button>Add Item</Button></IconProvider>

Accessing properties of a react component from the parent

I want to bundle some data together with a component. Here is an example of a SFC that has a property called name. I do not want to use the property name with the component named MyFormTab. Instead I would like to access this property from the parent component and assign it to be displayed within the parent.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
I would then like to render this component inside a parent and use the name property for another purpose
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name='Tab1' />
const Tab2 = <MyFormTab name='Tab2' />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1.name</h2>
<h2 onClick={setCurrentTab(2)}>Tab2.name</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
Instead of an SFC, I could also use a class I'm thinking.
class MyFormTab {
constructor(name){
this.name = name
}
render(){
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='email'></input>
</div>
</>
)
}
}
My project is predominantly using hooks however. My team lead(who doesn't know React much) will probably be hesitant towards mixing class components with hooks. I've read on other posts that hooks can basically replace class components in most situations. I don't know how hooks could be better, or even be used in this situation.
What do you think would be a good way to do what I am trying to do? Is putting SFC's with hooks and class components into the same project a good idea? Am I looking at this whole thing wrong?
Thank you
In react props are passed only from parent to child. So you can just have a parent with that name value and passed it down if you want to.
Edited my answer to respond to you edit.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name=`Tab1` />
const Tab2 = <MyFormTab name=`Tab2` />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1</h2>
<h2 onClick={setCurrentTab(2)}>Tab2</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
To you question about mixing class based and function components. You can't use hooks with class based components so don't, and there is no need to. I think you should learn more about the basics of react. If you need to share data with other components, the data should be in the parent component, passed to children or in a React context.

Swapping HTML element with React.js through props

Context: New to React. Wanting to build a versatile <Wrapper /> component.
My question is: "How do I change the HTML element that is rendered, based on a prop's value?"
My component looks like the following:
const Wrapper = ({ elem = 'div', name, children }) => {
const statement = `<${elem} className='oc-${name}-wrapper'>{children}</${elem}>`;
return statement;
};
This quasi-solution just renders out the string. How might I write the above so that I could use the component with interchangeable elements like the following two examples, so that <main> and <div> would be implemented respectively? Currently the variables are successfully injected, but the code is rendered as a string, rather than executing.
// usage
<Wrapper elem="main" name="splash"><p>foo</p></Wrapper>
// output
<main class="oc-splash-wrapper"><p>foo</p></main>
// usage
<Wrapper name="content"><p>bar</p></Wrapper>
// output
<div class="oc-content-wrapper"><p>bar</p></div>
Thank you in advance!
HTML as String will not be rendered properly.
Try changing Wrapper function :
const Wrapper = ({ elem = 'div', name, children }) => {
const statement = `<${elem} className='oc-${name}-wrapper'>{children}</${elem}>`;
return <div dangerouslySetInnerHTML={{ __html: statement }} />;
};
Your Render method should look like this
class Wrapper extends React.Component {
render() {
const CustomTag = (`${this.props.elem}` !== 'undefined')?`${this.props.elem}`: "div";
const classIs = `os-${this.props.name}-wrapper`;
return <CustomTag className={classIs}>{this.props.children}</CustomTag>
}
}

react higher order component

Im looking into higher order functions and i dont really understand how this part works.
say i have the following function:
const withAdminWarning = WrappedComponent => {
return props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<WrappedComponent {...props} />
</div>
);
};
const Info = props => (
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
);
const AdminInfo = withAdminWarning(Info);
ReactDOM.render(
<AdminInfo isAdmin={true} info="There are the details" />,
document.getElementById("app")
);
From my understanding of components, to access the props variable, you have to use either props, if its a stateless component, or this.props if it is a class component.
From where does the props come into play in the example above as i cant get access to it from the WrappedComponent or anywhere else apart from the return statement.
The Higher order Component returns a function which is a functional component. Am I right in thinking that foo(Info) means withAdminWarning(Info)?
So after calling withAdminInfo the AdminInfo Component looks basically like:
const AdminInfo = props => (
<div>
{props.isAdmin && <p>This is private info. Please dont share!</p>}
<div>
<h1>Info</h1>
<p>This info is: {props.info}</p>
</div>
</div>
);

Categories

Resources