I have some grouped data that I would like to render like such:
RowGroupTableHeader
RowGroup1
RowGroup1-row1
RowGroup1-row2
RowGroup2
RowGroup2-row1
RowGroup2-row2
RowGroup3-row3
Normally, I would do a loop through the groups, and within each loop, I would have an internal loop to go through each of the rows.
However, because I'm working with accessibility, div structures are very strict. eg. you cannot just throw in a surrounding container around div[role=rowgroup]. i.e. each rowgroup needs to be at the same level.
As such, I cannot use the usual Array.map() nested within each other because after the first iteration, I am expected to close the return() and cannot start a new Array.map().
Anyone got any ideas how I can achieve this?
Is there a wrapper component out there that can render its contents without the wrapper? eg. {content}?
Thanks.
John.
I'm not sure I completely understand the question. But your last sentence leads me to believe that you want to to write a React class that renders its child directly, without wrapping it in an outer element like div? If so, that is possible, but ONLY if you pass a SINGLE valid React component in as a child.
The following should work:
class Foo extends React.Component {
constructor(props){
super(props);
}
render() {
if(React.Children.count(this.props.children) === 1){
// If there is only one child, return it directly, unwrapped
return React.Children.only(this.props.children);
} else {
// Otherwise, return all children wrapped in div
return <div className="foo">{this.props.children}</div>
}
}
}
...
// Later on...
<Foo><div>Hello, world!</div></Foo>
// Would render simply as
// <div>
// Hello, world!
// </div>
<Foo><div>Bar</div><div>Baz</div></Foo>
// Would render as
// <div class="foo">
// <div>Bar</div>
// <div>Baz</div>
// </div>
Related
Say I have a sub component responsible for handling some user input
function MyInputComponent(props: {...}) {
return <input>...</input>;
}
Now say I want to add a button to control that input, normally I would do:
function MyInputComponent(props: {...}) {
return <>
<input>...</input>
<button>...</button>
</>;
}
This works so far. But now I want to render the button at some other places (i.e. not within the same component). e.g. if we look at the final html, I want to have the button to be after another component:
<parent-component>
<input>...</input>
<some-other-component>...</some-other-component>
<button>...</button>
</parent-component>
Of course, one way is to pass in <some-other-component> to MyInputComponent for it to render, but I want that code separation as MyInputComponent has nothing to do with that other component. And I don't want the parent component to handle the parameters for the <button> either as all the control logic (e.g. onClick action, etc.) are local to MyInputComponent only.
Is there a way to return {<input>, <button>} to the parent component, so that it can render them at desire places?
Try to use array instead of fragment
const [MyFirstComponent, MySecondComponent] = () => {
return [
<h1>Element 1</h1>,
<span>Element 2</span>
];
}
I'm working on a ReactJS app and i'm a new comer.
I have a Component like this
class Type extends React.Component {
constructor(props) {
super(props);
this.state = {
name : props.type.name,
description : props.type.description,
price : props.type.price,
imageList : props.type.images,
mode : 'view',
// i'm cloning the whole object
clone : props.type
};
}
handleDeleteImage(event) {
const imageId = event.target.getAttribute('data-imageId');
// get the current imageList of this Component
var imageList = this.state.imageList;
// checking the length of 2 image list before removing the
// targeted image
console.log(imageList.length) // displays 3
console.log(this.state.clone.images.length) // displays 3
// remove the targeted imageId
imageList.splice(imageId, 1);
// checking the length of 2 image list after removing the
// targeted image
console.log(imageList.length) // displays 2
console.log(this.state.clone.images.length) // displays 2
}
}
So what i'm doing here is i want to clone the object so when the user changes there mind and doesn't want to make changes anymore, they can hit the cancel button and everything is back to the state they were before (i have a function to handle this as well. I set the fields -name, description, price- to the values of the clone)
But as you can see, i didn't touched the image list in the clone at all still it got changed anyway.
Am i doing anything wrong here?
Thank you for any help.
Hey guys! So I realized that the concept I used in this service is not so efficient.
Like #Michael McQuade said, I should control the data in one flow only which is changing the data in the parent Component, not the child ones. I also reviewed the ReactJS Documentation and I can see why.
But with that being said. Let's say I'm working on a Component which has lots of Child-Component, does that mean I have to callback all the way up to the Parent Component to make changes in the Child one? And does that mean i must have multiple handlers in the Parent one that will be passed down to the Child that needs them?
I hope my question doesn't border you guys. Thanks!
You're using state and props together in a way I wouldn't recommend.
Instead of trying to make a copy of the props and storing it as state, make a stateless function and pass down a function which handles the deletion.
Here is an example:
class Child extends React.PureComponent {
render () {
return (<button onClick={this.props.handleBye}>{this.props.text}</button>)
}
}
class Parent extends React.PureComponent {
state = {
text: "Hello"
}
handler = () => {
this.setState({text: "bye"})
}
render() {
return (<Child text={this.state.text} handleBye={this.handler} />)
}ˆ
}
ReactDOM.render(<Parent />, document.body)
<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>
This line will not clone an object, rather it will create a reference
clone : props.type
To clone you can use various techniques (depending on your need) one simple one would be
clone: Object.assign({}, props.type)
beware that this will only create a shallow copy of the object.
To create a deep copy you can use
clone: JSON.parse(JSON.stringify(props.type))
this is an easy technique but it is slow and will not copy dates correctly.
If you need fast and reliable deep clone you better search for something else that suits your needs (maybe a library like lodash).
this.state.clone is just a reference to the props.type object. So when you use splice() you change the contents of the array and therefore "mutate" props.type.
If you really want to clone the object do it like that:
this.state = {
clone: {...props.type} // create a new object and spread the props.type object properties
}
You can read more about the spread operator here
In React we are loading a list of children components that define their own ID. We want to then do a pass where we re-arrange the children based on their set internal ID. How do we communicate the internal ID to the parent? Also it would be nice to use that ID as the parent rendering ekey. Note the internal ID does not match the component name.
class Thing1 extends React.Component {
const ID = 'Thing1IDString';
}
class Thing2 extends React.Component {
const ID = 'Thing2IDString';
}
<Parent>
<Thing1 />
<Thing2 />
<Thing3 />
</Parent>
The first thing I'd suggest is trying to make the parent know how to compute the child IDs somehow. If the child IDs come from a database, then make the parent compute them instead of making the children compute them. The lower components in the tree should be less intelligent. Hand them everything they need to know in its final form, so they can simply render it.
The structure you're talking about, where the child has "private" data that the parent needs to act upon, is generally solved in React by:
Storing that data in the parent
Passing the data to the child as a prop
Passing an onDataChanged function to the child, so that the child can tell the parent when the data changes.
This is the "controlled input" pattern. It feels awkward at first, and may seem like there's too much indirection at first glance. But the advantage is that the data flow is very predictable.
Now, if that isn't possible in your case, and you really need the children to "register" with the parent, then you can use props or context to pass a registerChild function to the children. In the child, use its componentDidMount lifecycle method to call the registerChild function with its computed ID. The parent can then keep track of these IDs however it needs to.
Your syntax is kind of confusing. Let's convert the pseudo-example to an actual working ReactJS code sample.
Let's say you have a child(s) component(s):
class Thing1 extends React.Component {
constructor(props) {
super(props);
this.id = 'Thing1IDString';
}
render(){
return (
<p>Child component</p>
);
}
}
If you want to access Thing1's id property from your parent component, there are several ways how to do communicate between components.
It depends what you want to achieve. If you just want to access a child component property, you may use refs:
class Parent extends React.Component {
doSomething() {
// Access anything you need from `this.refs.thing1`
const thing1ID = this.refs.thing1.id;
}
render() {
return (
<div>
<button onClick={this.doSomething.bind(this)}>Get ID</button>
<Thing1 ref="thing1" />
</p>
);
}
}
I've touched above problem here: https://github.com/facebook/react/issues/15320
There are 3 methods you can use to achieve Parent child data transfer (context Api, mutatiin, Reac.Children deep traversal).
I currently have two React components one that maintains a user inputted list of objects ex: [{"good1":"$10"},{"good2":"$15"}] and another component that lets the user create a list and use the goods as tags. However I am having trouble on how to pass data that is in the state of GoodManage to ListManager, I also have a wrapper for the layout. I tried passing the state from the LayoutManager into the GoodManager and having it update the goods list, and send that same list to ListManager, but it only seems to work once.
class LayoutManager extends React.Component{
constructor(){
super();
this.state = {"goods":[{"good1":"$1"}]};
}
updateGoods(goods){
this.setState({"goods":goods});
}
render(){
return (
<div className="layout-manager">
<div className="good-manager container">
<GoodManager update={this.updateGoods.bind(this)} goods={this.state.goods}/>
</div>
<div className="list-mananger container">
<ListManager goods={this.state.goods}/>
</div>
</div>
)
}
}
As you described its working only first time, it means you are storing the goods value in the state variable of ListManager component in constructor, it will save only the initial list since constructor only get called on first rendering not on re-endering, so you need to use lifecycle method componentWillReceiveProps, it will get called when any change happen to props values, at that time you need to update the state values, use this method in ListManager component:
componentWillReceiveProps(nextProp){
this.setState({goods: nextProps.goods});
}
Reference: https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops
I figured I could do this, but I am getting this error:
TypeError: child.constructor.ConvenienceConstructor is not a function
I have a component in a page, ala:
// this content is in an html page. My component reads in this child, but I can't seem to modify any part of it.. Just diplay it.
<MyComponent prop1="somevalue">
<div className="myclass1"> some child content that is dynamic </div>
</MyComponent>
Now, in my component since that inner child(ren) is dynamic, I need to change that class depending on some condition. But I can't. I get that error I noted above.
I tried this:
var childContent = React.Children.map(this.props.children,
function(child) {
return React.cloneWithProps(child,
{ className: 'myNEWClass' } );
});
I tried cloneElement too, that didn't work either.
Doesn't work. I tried accessing the child directly, ala:
child._store.props.className // but can't seem to change it, seems immutable.
So, how can I change that class up?
thanks,
Props are supposed to be immutable, so instead of passing className as a prop, you make the parent pass in a prop named defaultClass. In your map method, you can add an extra prop, say, overrideClass. Finally, in the render() method of the actual child component, you need some logic to set the className to either overrideClass, or defaultClass. In this way, you don't have to mutate props.