Render multiple React components into a single DOM element - javascript

I'm looking to render multiple modals into a single ReactDOM element. Here's the HTML structure that React renders to.
<body>
<div id="modal-socket"></div> // Insert multiple here
<div id="wrapper">
// Other content goes here
</div>
</body>
There's a long story behind why I need to render multiple components into #modal-socket but I want to do something akin to this:
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
ReactDOM.render(<AddMeasurableModal />, document.getElementById("modal-socket"));
Obviously this replaces the current content of #modal-socket on each render call.. So I don't get my end result. Boo.
Did a search and found a few answers on it but none meet my needs.
Cheers.

As you told in a comment, the dynamic way would be something like this
Inside of a main component you could do:
Imagine having an array like:
let myArray = [
{
prop1: 'hello world'
},
{
prop1: 'Hey there!'
}
]
//Then in the render function (you can put that array into the state or something)
render(){
return (
<div>
{myArray.map((entry,index) => {
return <AddMeasurableModal key={index} {...entry} />
})}
</div>
)
}
this will create as many AddMeasurableModal components as there are entrys in the myArray variable and add every property stored as props onto the component (In this case, every AddMeasurableModal component has access to the this.props.prop1 value, because of the {...entry} spread syntax)
Notice how I only put myArray.map() into the render function inside of {}?
React renders every array of components without further configuration inside of the render function. And Array.map() returns an array. Just make sure to return only valid react elements! When doing this, don't forget to add a uniqe key prop to each element to avoid warnings.
EDIT: in this case, the key prop is the current index in the array, but when fetching data from a server I would recommend to use a uniqe id from the database or something to avoid rendering bugs.
If you don't want to map over an array, you can just set a number of components and then loop over them, creating an array of components and put them into the render function.

Wrap your multiple modals into 1 container and render that, eg:
let modals = (
<div>
<AddMeasurableModal />
<AddMeasurableModal />
<AddMeasurableModal />
</div>
);
ReactDOM.render(modals, document.getElementById("modal-socket"));

Related

React ref inside of a loop breaks on re render

I have a gallery component that takes in an array of components. In each of the child components I am assigning a ref. The reason for this is because within the child component there are many other children components and I am attempting to access some functions on a component that is about 5 component deep. The below code shows the initial setup:
export class Gallery extends React.Component {
render() {
const galleryItems = data.map((item, index) => {
return (
<GalleryItem
ref={React.createRef()}
/>
);
});
return (
<div >
<Gallery
items={heroGalleryItems}
/>
</div>
);
}
}
When the Gallery component renders all the refs in the array of GalleryItem component are correct. But as soon as the Gallery component re renders for any reason the refs in the GalleryItem components become null values.
I have tried several things in the children components but nothing I do fixes the issue. I believe the reason is because something is happening in the code above.
I have also tried to change up the code after reading the following:
Issue storing ref elements in loop
However its not really clear to me what the person is saying to do when I look at my own implementation.
You need to move out React.createRef() from the loop (and also render) as it is creating a new ref on every render.
Depending on your code/usage, you'd need to do this in constructor and CWRP methods (basically whenever data changes).
Then creating galleryItems would be like
...
<GalleryItem ref={item.ref} />
...

Share data between React components with no relation?

I'm working on a React component library that allows for client-side data filtering by passing an array of objects and an <input/> as props to a <SearchFilter/> component. I want to return the filtered results to a separate <SearchResults/> component that can be rendered elsewhere in the tree (i.e. the results component doesn't have to be a child of the input component).
I've got the filtering figured out, but I'm not sure the best route to take in React on getting the filtered data to the <SearchResults/> component.
This is what I'd like to end up with...
<SearchFilter
data={data}
input={<input type="text" value={value} onChange={this.handleChange}/>}
/>
Then, using Render Props to return the data and map over that to return JSX, there would be the results component. Something like this...
<SearchResults
render={data => (
data.map(el => (
<div>
<span>{data.someProperty}</span>
</div>
)
)}
/>
This is what I'd like to achieve because I want to allow for rendering the <SearchFilter/> component at one place in the tree, and allow the <SearchResults/> component to be rendered elsewhere, so that there's maximum flexibility in how the tree is composed and, therefore, how the view is rendered.
I've looked into the Context API, but it seems like that would require a handful more components to be a part of my library, which further complicates what I'm trying to achieve. If that's the only way around it, then that's fine, but I wanted to ask and see if anyone can think of another solution.
Thanks!
The bigger issue is that you will need to manage a state that is shared between components on a higher level, i.e., any component that will wrap these other two components, ultimately. With plain React, this state would be managed by the parent (or ancestor) component, passing down the relevant values as props. This opposed to the, usually bad, idea to have sibling components influence each other's state, since you well get into the "who's boss here"-problem.
The thing the Context API handles is not having to pass down props for things that typically don't change (or: typically shouldn't cause renders to trigger often).
A global state store, such as Redux, can help you modelling this, but in essence it's not much more than 'a' component managing state, and other components rendering according to that state. Events within the lower components trigger changes in the data, which will cause the state to change, which will cause the props of the children to change, which then will cause re-renders.
I'd advise you to try using this simple pattern:
class Search ... {
state = {data: [], text: ""}
render() {
return (
<div>
<SearchFilter
data={this.state.data}
onSearch={() => this.fetchNewData()}
onChange={(e) => this.setState({text: e.targetElement.value})}
text={this.state.text}
/>
<SearchResults data={this.state.data} />
</div>
);
}
fetchNewData() {
fetch('/url?text=' + this.state.text)
.then((newData) => { this.setState({data: newData}); })
}
}
Something along these lines. If you have trouble modelling stuff like this, you can use Redux to force you to do it in a similar way, and avoid managing local state intermixing with global state (which is typically something that is hard to manage).
If you do this right, components that have no state (i.e., aren't responsible for managing state and thus have no event handlers) can all become pure components, i.e. stateless components, i.e. functions that return JSX based on props:
const SearchResults = ({data}) => <div>{data.map( () => <etc /> )}</div>
You could create a data store class that holds your filter, pass it in as a property to both components, and have your SearchFilter component change a value in that.

Exporting variables to siblings elements

Im creating simple test React app.
I've got a small database filled with JS objects. When user types query into a searchbar and presses enter i export this.state.form (string from searchbar) to a variable. Then I iterate through database and if I find a record's name equal to form variable I want to display the details of this object in a div.
However, this div component is in another, sibling JS file and I cant figure out how to effectively export variables and their values to sibling files without Redux.
With pseudo code it would be something like this:
var isFound;
var arrayNumber;
// I iterate through the array of objects with for loop to find correct object and store "i" to arrayNumber and want to export it
// If record is found i set isFound to true, otherwise to false
In a new file I render a simple div, where I want to do the following
//if isFound is false
<div>Not found</div>
//if found
<div>database[i].details</div>
Try to create parent components for these 2 components.
For example, now your structure is smth like
<Component>
<Subcomponent />
<Subcomponent />
... other content
</Component>
You should change it to
<Component>
<ParentSubcomponent>
<Subcomponent />
<Subcomponent />
</ParentSubcomponent>
... other content
</Component>
And leave to ParentComponent to handle search & display using callbacks for Subcomponents (change ParentComponent's state on Subcomponent onClick on search button, that will rerender second Subcomponent because of provided props, that are get from ParentComponent's state (actually it's search result), have been changed, so save data about search & display info in ParentSubcomponent's state.
So single source of truth will be ParentSubcomponent's state, 1st Subcomponent uses callback to update it with the search result, 2nd Subcomponent gets search result (from state) as a props and updates itself.

How change state or property of an outer / separate component in reactjs?

Current DOM tree is like:
<comp1>
<div>
<comp2></comp2>
<comp3>
<comp4 />
<comp4 />
.........
.........
.........
</comp3>
</div>
<comp5></comp5>
</comp1>
Component5 i.e. is my modal. I want to set it's state and props by onclick event on
My objective is to display the detail data on a modal based on the selection.
So I need to set the state and props of modal component accordingly. How can I that in the current structure?
The old fashioned way, which is fine for a small page, is to move all states into the root node (comp1). Other components become stateless. The root node passes to its children the properties they want to render, alongside the setters required to mutate the properties. For instance, comp5 gets a property username which is just the value of comp1.state.username, plus a property setUsername, which is a function taking a username string parameter in which comp1 uses setState to update its state's username. This way comp5 can render and update the username, and other components are aware of the change (setState triggers a render of children).
For more complex apps, passing all these properties to children gets tedious, and you can resort to framework like flux and redux.
i think standard way of doing it is something like this
in your react component:
constructor(props) {
...
this.state = {selectedComp4: null
...
}
}
...
handleSelectedComp4Change (yourData) {
this.setState({selectedComp4: yourData})
}
...
render() {
...
return (
<comp1>
<div>
<comp2></comp2>
<comp3>
<comp4 onSelectedComp4Change = {this.handleSelectedComp4Change}/>
<comp4 onSelectedComp4Change = {this.handleSelectedComp4Change}/>
...
</comp3>
</div>
<comp5 SelectedComp4={this.state.selectedComp4}></comp5>
</comp1>
)
}
in comp4 send your data: onClick={() => { this.props.onSelectedComp4Change(someData) }}
in comp5 use your data in this.props.SelectedComp4
EDIT:
just like #Valéry said

React: Bubbling up click events on nested components

I'm creating a react file tree, and I have the tree setup as a React component. The tree can take a contents prop that is an array of either strings, or other <Tree /> components (this enables the nested file structure UI). These tree components can be nested indefinitely.
I need to register a click event on the children of the nested tree components, but I'm having trouble getting it to work beyond the first level of nesting. A simplified example of what I'm dealing with:
//In App - the top level component
const App = React.createClass({
_handleChildClick () {
console.log("this is where all child clicks should be handled");
},
render () {
return (
<Tree
handleChildClick={this._handleChildClick}
contents={[
<Tree />
]}
/>
);
}
});
//And in the tree component
<div onClick={this.props.handleChildClick}></div>
If you want to see more detail - here's the github repo.
I tried researching this question and saw people using {...this.props} but I'm not sure if that applies to my scenario - if it does, I couldn't get it to work.
Thanks for any help on this.
The reason why the click handling does not work beyond the first level is because your second level Tree component (the one inside the contents array) does not get the appropriate prop handleChildClick passed in. (BTW I think the convention is to call the prop onChildClick while the handler function is called handleChildClick - but I digress.)
Do I understand correctly that you actually want to inform each layer from the clicked component up to the top? For this to happen, you need to extend the props of the tree component that is inside the contents array - it needs to receive the click handler of its parent component. Of course, you cannot write this down statically, so it needs to be done dynamically:
Your Tree component, before actually rendering its children, should extend each of them with the component's click handler, which can be done using the function React.cloneElement (see API documentation and a more detailed discussion). Directly applying this to your component makes things a bit messy, because you are passing the component's children in a prop, so you need to figure out which prop to modify. A bit of a different layout would help you quite a lot here:
<Tree handleChildClick={this._handleChildClick}>
<Tree />
</Tree>
looks nicer IMHO and makes the structure much clearer. You can access the inner components via this.props.children, and cloneElement will be much simpler to use.
So, in your Tree component, you could have a render method like this:
render () {
const newChildren = this.props.children.map(child =>
React.cloneElement(child, {onChildClick: this._handleChildClick}));
return (
<div>{newChildren}</div>
);
}
Please note that this code will not work if you have a mixture of strings and Tree components, therefore my third and last suggestion would be to wrap those strings into a very thin component to allow for easier handling. Alternatively, you can of course do a type comparison inside the map.

Categories