React Transferring Props except one - javascript

React suggests to Transfer Props. Neat!
How can I transfer all of the props but one?
render: function(){
return (<Cpnt {...this.propsButOne}><Subcpnt one={this.props.one} /></Cpnt>);
}

You can use the following technique to consume some of the props and pass on the rest:
render() {
var {one, ...other} = this.props;
return (
<Cpnt {...other}>
<Subcpnt one={one} />
</Cpnt>
);
}
Source

What you need to do is to create a copy of the props object and delete the key(s) you don't want.
The easiest would be to use omit from lodash but you could also write a bit of code for this (create a new object that has all the keys of props except for one).
With omit (a few options at the top, depending on what package you import/ES flavor you use):
const omit = require('lodash.omit');
//const omit = require('lodash/omit');
//import { omit } from 'lodash';
...
render() {
const newProps = omit(this.props, 'one');
return <Cpnt {...newProps}><Subcpnt one={this.props.one} /></Cpnt>;
}

If you have a lot of props you don't want in ...rest e.g. defaultProps, it can be annoying to write all of them twice. Instead you can create it yourself with a simple loop over the current props like that:
let rest = {};
Object.keys(this.props).forEach((key, index) => {
if(!(key in MyComponent.defaultProps))
rest[key] = this.props[key];
});

Thank you #villeaka!
Here's an example of how I used your solution for other people to better understand it's usage.
I basically used it to create a stateless wrapping-component that I then needed to pass its props to the inner component (Card).
I needed the wrapper because of the rendering logic inside another top level component that used this wrapper like this:
<TopLevelComponent>
{/* if condition render this: */}
<CardWrapper {...props}> {/* note: props here is TLC's props */}
<Card {..propsExceptChildren}>
{props.children}
</Card>
</CardWrapper>
{/* if other condition render this: */}
{/* ... */}
{/* and repeat */}
</TopLevelComponent>
where several conditions determine what comes after the H4 in the wrapper (see actual rendered node tree below).
So basically, I didn't want to duplicate code by writing the entire part that comes before {children} in the example below, for each arm of the conditional in the top level component that renders multiple variants of the wrapper from above example:
const CardWrapper: React.FC<IRecentRequestsCardProps> = (props) => {
const { children, ...otherProps } = props;
return (
<Card {...otherProps} interactive={false} elevation={Elevation.ONE}>
<H4>
Unanswered requests
</H4>
{children}
</Card>
);
};
And concrete usage in a React render function:
if (error)
return (
<CardWrapper {...props}>
<SimpleAlert title="Eroare" intent={Intent.DANGER}>
{error}
</SimpleAlert>
</CardWrapper>
);
if (loading)
return (
<CardWrapper {...props}>
<NonIdealState
icon="download"
title="Vă rog așteptați!"
description="Se încarcă cererile pentru articole..."
/>
</CardWrapper>
);
if (!data)
return (
<CardWrapper {...props}>
<NonIdealState
icon="warning-sign"
title="Felicitări!"
description="Nu există cereri fără răspuns."
/>
</CardWrapper>
);
// etc.
So the above just adds the H4 header before the children of the wrapper and also passes down the props that it has been passed down to, to the inner Card component.

The simplest way I found so far:
const obj = {
a: '1',
b: '2',
c: '3'
}
const _obj = {
...obj,
b: undefined
}
This will result in _obj having all the props except b

Try this:
function removeProps(obj, propsToRemove) {
let newObj = {};
Object.keys(obj).forEach(key => {
if (propsToRemove.indexOf(key) === -1)
newObj[key] = obj[key]
})
return newObj;
}
const obj = {nome: 'joao', tel: '123', cidade: 'goiania'}
const restObject = removeProps(obj, ['cidade', 'tel'])
console.log('restObject',restObject)
restObject
{
nome:"joao"
}

I had this issue when extending Material UI. A component would emit a warning if an unknown property was passed at all. I solved it slightly differently by specifically deleting the properties I didn't want to pass:
const passableProps = { ...props } as Partial<typeof props>;
delete passableProps.customValidity;
return (
<TextField { ...passableProps } // ...
);

Related

Can I store a component in a variable and push children to it after it has been created?

Let's say that I have this component:
const Test = ({ children, ...rest }) => {
return <>{children}</>
};
export default Test;
I am wondering if it is possible to create a variable that holds the component like this:
const test = <Test></Test>;
And then loop over some data and push children to the test variable on every iteration.
if you don't have the data yet, then all you have to do is conditionally render your component when you do have the data.
{ data ? (<Test>{data.map(...)}</Test>) : <SomeOtherComponent /> /* or null */}
or
{ data ? <>{data.map((x) => <Test>{x}</Test>)}</> : <SomeOtherComponent /> /* or null */}
depending on what you want achieve, i didn't fully understand your question
i.e. if you have the data you need, render the component, rendering the children as you see fit, otherwise render some other component (or null, to render nothing)
Yeap, try that pattern:
const test = (children) => <Test>{children}</Test>;
and usage
<>
{[1,2,3].map(el=>test(el))}
</>
[Edited]
const TestComp = ({children}) => <Test>{children}</Test>;
<>
{[1,2,3].map(el=>(<TestComp>{el}</TestComp>))}
</>

Update interface element to follow external Object in React

I'm pretty sure my question is standard but I did not find the answer. I created a CodePen to illustrate it : https://codepen.io/Hylectrif/pen/QWdoqNB?editors=1111.
I am actually trying to make my interface correspond to an external object by mapping one of it attributes. But my interface does not automatically update on change. I am probably missing a hook or something but I can't find out.
Thank for your help ☺
class Example{
constructor(){
this.r = [1, 2, 3]
}
updateR = () =>{
this.r.pop()
alert(this.r)
}
}
function Welcome(props) {
const e = new Example();
return <React.Fragment>
<button onClick={e.updateR}>yo</button>
{
e.r.map(bar => {
return (<h1 key={bar}>{bar}</h1>)
})
}
</React.Fragment>
}
const element = <Welcome />;
ReactDOM.render(element, document.getElementById('root'));
Your component never gets re-rendered, because you don't have any props changing and you don't have a state to trigger change detection.
To make this work you need to use the useState hook and update the object held inside that state. It's better to have functionality inside the component rather than another object.
Although it's not optimal something like this would work:
class Example{
constructor(){
this.r = [1, 2, 3]
}
updateR = () =>{
this.r.pop()
alert(this.r)
return this;
}
}
function Welcome(props) {
const [e, setE] = React.useState(new Example());
return <React.Fragment>
<button onClick={() => setE({...e.updateR()})}>yo</button>
{
e.r.map(bar => {
return (<h1 key={bar}>{bar}</h1>)
})
}
</React.Fragment>
}
const element = <Welcome />;
ReactDOM.render(element, document.getElementById('root'));

Is it mandatory to pass props to react functional component?

I have a functional react component. It does not use any props, it just return elements. Have I pass props to it anyway? Are there any agreements about that? Will be code below valid React code?
const HelloComponent = () => (
<div>Hi!</div>
);
No you don't. props are explicitly passed to each component. If you don't use any property from it, just don't declare it. Exactly like in your example. Consider the following
const App = () => <Child />
const Child = props => {
console.log(props) // { }
return <div>hey</div>
}
In this case props is just an empty Object and there is no need to declare the argument. If you don't need to read from it, don't even mention it
const Child = () => <div>hey</div>
It is completly valid, there is no need for props. Its the same as with any other function, if it doesnt have arguments, dont give it any. :-)
As functional react components are just javascript functions the same rules apply to them as they do for any function. You can safely omit unused arguments.
The code you've mentioned is valid.
Pattern A
// span on single line and returns by default..
const Test = () => <div>Hello</div>
Pattern B
// span on multiple lines, still returns by default a single node
const Test = () =>
<div>
Hello
</div>
Pattern C
//span on multiple lines, but grouped by a paranthesis
const Test = () => (
<div>
Hello
</div>
)
WRONG..
//tryin to group with braces..
const Test = () => {
<div>
Hello
</div>
}
Pattern D:
// group by braces, so you have to return explicitly
const Test = () => {
const greeting = 'hello'
return (
<div>
{greeting}
</div>
)
}
Note:
it's not possible to return multiple nodes in react. all the nodes must have a single parent as it's root. why {} braces fails is because, it is used to group multiple nodes or code fragments.
//container
//destructure props from other end
render() {
const obj = {
name: 'john',
isAdmin: true,
}
return <Child {..{ obj }} {...this.props} />
}
// child: destructure from here
const Child = ({ obj, ...props }) => {
const { value1-in-props, value2-in-props } = props
}
//container
//destructure whilst sending
render() {
const obj = {
name: 'john',
isAdmin: true,
}
return <Child {..obj} {...this.props} />
}
// child: destructure
const Child = ({ name, isAdmin, ...props }) => {}

React - What is the difference between stateless function component and common component

What's the difference between stateless function component and common component when rending in React ?I couldn't understand very well! How can I get details about the difference between them! Thank you!
// Stateless function components
const renderIcon = result => {
return <Icon type="icon-fail"/>;
};
const Page = props => {
// ...
return (
<div>
...
{renderIcon(props.result)}
...
</div>
);
};
const ResultIcon = ({result}) => {
return <Icon type="icon-success"/>;
};
const Page = props => {
// ...
return (
<div>
...
<ResultIcon result={props.result} />
...
</div>
);
};
some codes I added some codes in project, there two methods to render component but perfect-scrollbar display differently. I don't know why!
// parts of codes
const data = [...data] // ...some dates used
return <div className="parts_codes">
{
[
{
title: 'Table',
components: [
{
dataType: 'basiclist',
DataComponent: ({mainTableData}, index) =>
<MainTable
loading={mainTableDataStatus}
// ...
/>,
}
]
},
{
title: 'String',
components: [
{
dataType: 'Object',
DataComponent: ({type}, index) => (!type || status)
?
<Spin key={index} />
:
<div key={index}>
<PlotComponent />
</div>
}, {
dataType: 'Object',
DataComponent: ({type}, index) => (!type || status)
?
<Spin key={index} />
:
<div key={index}>
key={index}
>
<ColumnComponent />
</div>
}
]
},
].map((item) => <div key={item.title} className="map_parts">
<span>{item.title}</span>
{
item.components.map((details, index) => {
const {dataType, DataComponent} = details;
return <DataComponent index={index} data={data}/>
// one component perferc-scrollbar instance failed
// return DataComponent(data, index);
// another functional component perferc-scrollbar instance success
})
}
</div>)
}
</div>
class MainTable extends Component {
componentDidUpdate() {
// ... do perferc-scrollbar instance
}
render() {
return <Table />
}
}
After Question edit
From the comments I gather your actual question is why is that exact snippet of code you provided failing.
It looks like this is the part you're having trouble with. Though I'm not sure. The comments around it are unclear. But I think it doesn't work as it is now.
item.components.map((details, index) => {
const {dataType, DataComponent} = details;
return <DataComponent index={index} data={data}/>
// one component perferc-scrollbar instance failed
// return DataComponent(data, index);
// another functional component perferc-scrollbar instance success
})
details.DataComponent appears to be a component generating function. You could try just changing your return <DataComponent... to return details.DataComponent({type: "Whatever type value is"}, index);
However, that code seems to be mixing data and components. Overall I'm not sure what you're doing or trying to do but with my limited knowledge I'd expect something more like:
const DataComponent = ({type,index, status, data}) => {
if(!type || status) {
return <Spin key={index} />
}
else {
// I assume plot component needs the 'data'
return <div key={index}><PlotComponent data={data}/></div>
}
}
Then each of your DataComponent: () => ... assignments would become: data:"The data object for the graph".
And finally, your return statement in your inner map statement would be return <DataComponent type={"Whatever type is"} status={"Whatever status is"} index={index} data={details.data} />;.
Old answer
Those all are stateless functional components. Where did you hear of 'common components'? That's not a thing I've heard of. If you could provide some links to the material you are reading it would be helpful.
The other way of defining components is to inherit from React.Component:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Functional components, like the ones in your examples, are just a function; the same as would be in the render() of a component built with inheritance.
Regarding 'stateless': In programming, 'state' is a concept that refers to data that is carried over from one execution to the next. Stateless components don't carry any data. They can't because they are just a function not a class.
This makes them simpler but more predictable. Send the same parameters to the stateless component multiple times, you will get the same result.
This is more clear with examples. Many are provided here in the official documentation.
basically we use stateless functional Components when we doesn't need to use a life cycle method and neither the state, so that's why it's called stateless(without state). So we do not need to inherit from React.Component class, all we need is to return JSX directly with props data.
I recommend that you take a look at the react docs.

How to pass extra props down to children of arry.map

I have been trying to pass extra props own to the children being created with the .map function but I have not been able to succeed in passing them succesfully.
This is my code:
export const CommentsListShanghai = (props) => {
const newTimestamp = props.timestamp;
console.log(newTimestamp);
const comments = props.comments;
if (comments.length > 0 ) {
return (
<ButtonToolbar className="comment-list">
{comments.map((com) => {
return (
com.adminSpark ?
<CommentsModal
className="comments-modal"
data-comments-modal={props.newTimestamp}
key={ com._id }
comment={ com }
city={com.city}
person={com.person}
location={com.location}
title={com.title}
content={com.content}
fileLink={com.fileLink}
timestamp={com.timestamp}
createdBy={com.createdBy}
/> :
<CommentsModal
key={ com._id }
comment={ com }
city={com.city}
person={com.person}
location={com.location}
title={com.title}
content={com.content}
fileLink={com.fileLink}
timestamp={com.timestamp}
createdBy={com.createdBy} />
)
})}
</ButtonToolbar>
);
} else {
return (
<Alert bsStyle="warning">No sparks yet. Please add some!</Alert>
);
}
};
CommentsListShanghai.propTypes = {
comments: React.PropTypes.array,
};
I am able to pass all the props of the comments const that I created, the problem is that besides these props I also need to pass an extra prop which is available in the CommentsListShanghai. How am I able to pass an extra props to this array?
I am able to console.log(newTimestamp) without a problem but don't understand how I can pass it down to the .map function.
Instead of
data-comments-modal={props.newTimestamp}
just use
data-comments-modal={props.timestamp}
The props here is still referring to the context of CommentsListShanghai.

Categories