I am looking for a way to get all children out of a React Component.
Example:
class MyComponent extends React.Component {
render() {
<div>
<h1>Welcome!</h1>
<ChildComponent />
</div>
}
}
const renderedComponent = <MyComponent />
React.Children(renderedComponent.props.children).forEach(() => {
// Some logic
});
Problem: renderedComponent.props.children is empty. Is there any way to get the children in a way like this ?
My goal is to get all children recursively.
Take a look at ReatDOMServer It seems like you will want to render your component to a string via renderToString and then parse the results with something like jQuery.parseHTML
Related
I am using microfrontend, In that I have a main container which uses these two Microfrontend component, and these three component are in different repo.
I have one React component in one repo:
class CustomForm extends React.Component {
...
render() {
return (
<div>
{this.props.component}
</div>
);
}
}
And I have a different component In another repo(other Microfrontend component) that use it:
class SomeContainer extends React.Component {
render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
component={<SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>}
object={someObjectVariable}
/>
);
}
}
Is there a way to pass props between two different Microfrontend component?
Your proptype for component in CustomForm will need to accept ReactNodes.
But you should be able to pass it through as a function (Meaning without the < and />:
if you do not need to pass any custom props, you can just call
<CustomForm component={SomeInnerComponent} .... />
If you need the custom props, you can wrap it in another function and just pass that through:
<CustomForm component={ () => {
return <SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>;
}} ...... />
The reasoning behind this is that because Functional components in React are actually just Functions, you can pass through a component as a prop without the < or /> and then it will just execute that as a function.
import SomeComponent from 'Somewheere';
class MyPage {
render() {
return '<OtherComponent AcceptsSomeClass={SomeComponent} />';
}
}
in OtherComponent I want to be able to do
class OtherComponent {
render() {
return <this.props.AcceptsSomeClass open={true} someOtherProp={123}/>;
}
}
I want to be able to render SomeComponent inside OtherComponent. I know I can just pass a node or a function. But I've seen a library before that accepts a class like this and I want to pass the class so that I can control it more in OtherComponent instead of deciding how it renders in MyPage and passing it thee node/function.
In other words I want to pass a class (react component) as a prop and then be able to use it in the JSX.
What I did is that we are passing a function that renders a component, then we can call that function inside the OtherComponent to render it there.
class MyPage extends React.Component {
render() {
return <OtherComponent AcceptsSomeClass={() => <SomeComponent />} />
}
}
class OtherComponent extends React.Component {
render() {
return (
<div>
<p>Content inside OtherComponent</p>
{this.props.AcceptsSomeClass()}
</div>
)
}
}
class SomeComponent extends React.Component {
render() {
return <h1>HELLO WORLD!</h1>
}
}
ReactDOM.render(<MyPage />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
An example of which you can pass components as props is when you are dealing with HOC (higher-order-components)
I use HOC to handle HTTP requests, for instance, using a modal to pop up on the screen with the loading / error when fetching /putting data, or authentication.
I will present you with a simple example:
import React from 'react'
import Modal from 'modal' //this would be a modal covering the screen
const httpHandler = WrappedComponent => {
const wrappedComponent = props => {
//handle some logic here, coded here, or as a result from some middleware
return (
<Fragment>
<Modal>...</Modal> //handle the HTTP async stuff here, like loading,
//or authentication, or an error message
<WrappedComponent {...props} />
</Fragment>
)
}}
You can call this inside a Component like this when you export another component
//all of the component stuff above
export default httpHandler(WrappedComponent)
I may be over thinking this, but I am curious if importing a child component directly is bad practice with regards to coupling and testing.
Below is a simple example:
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
<Header></Header>
<div>{this.props.importantContent}</div>
</div>
)
}
}
To me it looks like there is now coupling between Widget and Header. With regards to testing, I don't see an easy way to mock the Header component when testing the Widget component.
How do other larger React apps handle cases like this? Should I pass Header in as a prop? If using react-redux, I can inject header with the Connect method like below to reduce boilerplate. Is that sound?
import { connect } from 'react-redux';
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
{this.props.header}
<div>{this.props.importantContent}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
header: Header
}
}
export default connect(mapStateToProps)(Widget)
I am interested is simple doing what the community is generally doing. I see that one solution is doing shallow rendering to test on the main part of the component and not the child components using something like Enzyme.
Thoughts or other ideas?
Passing elements / components as props is a good idea. Having default props is a good idea too:
const Widget = ({
header = <div>Default Header.. </div>,
content = <div>Default Content.. </div>
}) =>
<div>
{header}
{content}
</div>
Then elsewhere in your app:
<Widget header={<Header title="Foo" />} content="content from props" />
No need to inject using connect
You can also pass a component, not just an element if you want to interact with props / send data back to parent:
const Widget = ({
Header = props => <div>Default Header.. </div>,
Content = props => <div>Default Content.. </div>
}) =>
<div>
<Header />
<Content />
</div>
Elsewhere:
<Widget Header={Header} Content={props => <Content />} />
As long as the component always renders the same thing it can be directly rendered as a child rather than the parent.
If all other portions of the Component remain constant and only the Header can be different across pages then you could actually implement it as an HOC instead of passing it as a props
const MyCompFactory = ({CustomHeader = DefaultHeader}) => {
return class Widget extends React.Component {
render() {
return (
<div>
<CustomHeader/>
<div>{this.props.importantContent}</div>
</div>
)
}
}
}
and use it like
const CustomComponent = MyCompFactory({CustomComponent: Header})
as long as testing is concerned in your case, you could just shallow render your component and then Search if the Header component is rendered something like
import Header from 'path/to/header'
const component = shallow(
<Widget {...customProps}/>
)
test('test' , () => {
expect(component.find(Header).exists()).toBe(true)
})
I'm working on a project that uses react-typeahead and attempting to implement a custom component for the dropdown list. React-typeahead accepts a customListComponent prop.
However, I need to pass a prop to the component that is being passed into the Typeahead component. Initially, I tried setting a variable as a the custom component:
//MainSearch.js
import SearchOrderComponent from './SearchOrderComponent'
export default class MainSearch extends React.Component {
//Constructor here
render() {
let customList = <SearchOrderComponent ranking={this.state.ranking} />
return(
<div className="search-container">
<Typeahead customListComponent={customList} />
</div>
)
}
}
This caused an invariant violation, with react stating that a react component was expected. My current workaround is to make SearchOrderComponent a function that accepts the paren't state as an input and returns a react component, like so:
//SearchOrderComponent.js
const wrapper = function(ranking){
let SearchOrder = React.createClass({
render: function() {
var searchRanking = ranking.map(function(item){
return <li key={item.key}>{item.value.niceName}</li>
});
return(
<div className='main-dropdown'>
{searchRanking}
</div>
);
}
});
return SearchOrder;
}
module.exports = wrapper;
Now I can input this function directly into the typeahead component:
//MainSearch.js
<Typeahead customListComponent={SearchOrderComponent(this.state.ranking)} />
But this feels like a break from the component API. Is there a more direct/proper way to do this?
If I understand correctly what you're trying to achieve, the most direct way would be
render() {
return(
<div className="search-container">
<Typeahead>
<SearchOrderComponent ranking={this.state.ranking} />
</Typeahead>
</div>
)
}
You can access the SearchOrderComponent from within the Typeahead component via this.props.children.
wonder if it is possible to pass a component a property as following
ReactDOM.render(
<ContainerBox anotherComponent={<AnotherComponent />} />, document.body);
And then insider the ContainerBox I want to pass AnotherComponent a property in following way.
class ContainerBox extends React.Component {
clickHandler() {
//does something fun
}
render () {
return (
this.props.anotherComponent(this.clickHandler) //<----- is it possible to pass properties from here?
);
}
}
Is it possible to pass things from ContainerBox to AnotherComponent from that position?
ContainerBox has a clickHandler function which I want to pass to AnotherComponent. It is possible to do so if I move <AnotherComponent /> to inside of render() instead. But then I cannot reuse ContainerBox for other components without first copying the whole ContainerBox.
Does it make sense? Hope you can understand.
UPDATED code example
Yes, that is possible. However, it's more common to do it like this
ReactDOM.render(
<ContainerBox><AnotherComponent /></ContainerBox>, document.body);
And in ContainerBox
class ContainerBox extends React.Component {
render () {
return (
this.props.children
);
}
}
Read more about reacts this.props.children here: https://facebook.github.io/react/docs/multiple-components.html#children
Edit:
I just want to point out that in this example, we are not passing a component, but an element (the result of rendering the component).
It's also possible to pass components, like this:
<Foo buttonComponent={FancyButtonComponent} />
and in Foo:
render() {
Button = this.props.buttonComponent;
return (
<div>
...
<Button />
</div>
);
}