Destructuring in React props, don't understand {...{ [resourceName]: item }} inside props - javascript

I came cross this code, and I don't understand {...{ [resourceName]: item }} inside the props inside the RegularList.js file.
I have two questions:
Whouldn't we, syntaxwise, write sth.=sth.
Which object is this {...{ [resourceName]: item }} trying to copy here?
Here's my code:
RegularList.js
export const RegularList = ({
items,
resourceName,
itemComponent: ItemComponent,
}) => {
return (
<>
{items.map((item, i) => (
<ItemComponent key={i} {...{ [resourceName]: item }} />
))}
</>
);
};
App.js
const products = [{
name: 'Flat-Screen TV',
price: '$300',
description: 'Huge LCD screen, a great deal',
rating: 4.5,
}, {
name: 'Basketball',
price: '$10',
description: 'Just like the pros use',
rating: 3.8,
}, {
name: 'Running Shoes',
price: '$120',
description: 'State-of-the-art technology for optimum running',
rating: 4.2,
}];
<RegularList
items={products}
resourceName="product"
itemComponent={SmallProductListItem} />
SmallProductListItem.js
export const SmallProductListItem = ({ product }) => {
const { name, price } = product;
return (
<h3>{name} - {price}</h3>
);
}

{ [resourceName]: item }
Is creating a new Object with a dynamic key resourceName
...{}
Is spread operator.
So what is achieve here, is you pass a dynamic prop resourceName to your component.
For example, if resourceName === "foo" your ItemComponent will have the prop foo:
<ItemComponent foo={item} />

Related

Getting content of currently active Text component wrapped inside popover of antd

I am using antd components for my react app. I have a Text component wrapped inside of Popover component. Now in my case this Popover is applied to one particular column of table, i.e. every row-element in that column has a Popover component rendered for it upon mouse hovering.
title: "Name",
dataIndex: "name",
key: "name-key",
sortType: "string",
sortDirections: ["descend", "ascend"],
sorter: (a, b) => a.name.length - b.name.length,
render: (text, record) => (
<Popover>
<Text onMouseOver={handleOnMouseOverCommitId}> {name} </Text>
</Popover>
)
I want to get hold of the row-element's value, the one contained by the above Text component whenever I hover over it. In this case the value denoted by {name} above.
I tried getting it with e.target.value via onMouseOver event, but it returned undefined.
I think I get the reason behind it, because the event.target returns an html node of type <span>.
With a normal div element e.target.value has worked in the past for me. But doing the same thing with a predefined component like antd's Text seems a bit trickier.
Just to elaborate, the Popover has two buttons and based on which button user clicks, I need to render some other components, something like an overlay component.
But in order to do that I would also need to get hold of the text value which originally triggered the Popover.
Below is the code(most of the things removed for preciseness).
record.name is what I ultimately need to capture.
<Popover
content={
<>
<Space>
<Button onClick={showSomeOverlayPaneForName}>
{"View Details for record.name"}
</Button>
<Button href={"https://abc.xyz.com/" + record.role}>
{"View Role Details"}
</Button>
</Space>
</>
}
trigger={"hover"}
>
<Text style={{"color": blue.primary}} copyable={true} onMouseOver={handleOnMouseOverName}>{record.name}</Text>
</Popover>
The handleOnMouseOverName function(which doesn't work anyway) :
const handleOnMouseOverName = (e) => {
//console.log("e.target.value :--- ", e.target.value);
setCurrentActiveName(e.target.value)
}
And once my currentActiveName variable is set(via useState), I use that value inside my function showSomeOverlayPaneForName
const showSomeOverlayPaneForName = (e) => {
axios
.get(
`some-url`,
{
params: {name: currentActiveName}
}
)
.then((response) => {
setData(response.data);
}).catch(reason => {
//xyz
});
}
You need to pass on the record of the enclosing render function to the handleOnMouseOverName function.
Check the following example
import React from 'react';
import 'antd/dist/antd.css';
import './index.css';
import { Space, Table, Button, Popover } from 'antd';
const App = () => {
const data = [
{
key: '1',
name: 'John Brown',
address: 'New York No. 1 Lake Park',
role: 'admin',
},
{
key: '2',
name: 'Jim Green',
address: 'London No. 1 Lake Park',
role: 'user',
},
{
key: '3',
name: 'Joe Black',
address: 'Sidney No. 1 Lake Park',
role: 'manager',
},
];
const columns = [
{
title: 'Name',
dataIndex: 'name',
key: 'name',
render: (name, record) => {
const content = (
<>
<Space>
<Button
onClick={() => {
viewDetail(record);
}}
>
{'View Details for ' + record.name}
</Button>
<Button href={'https://abc.xyz.com/' + record.role}>
{'View Role Details'}
</Button>
</Space>
</>
);
return (
<>
<Popover content={content} title="Details">
<div
onMouseOver={() => {
handleOnMouseOverName(record);
}}
>
{name}
</div>
</Popover>
</>
);
},
},
{
title: 'Address',
dataIndex: 'address',
key: 'address',
},
];
const handleOnMouseOverName = (record) => {
console.log(record);
};
const viewDetail = (record) => {
console.log(record);
};
return <Table columns={columns} dataSource={data} />;
};
export default App;
Output:
I hope this helps.
From antd docs: https://ant.design/components/popover/#header
Apparently you're supposed to render the <Popover/> with a content={content}-prop
For example
const content = <div>Content to render under title</div>
const App = () => {
const value = "Text to hover";
return (
<Popover content={content} title="Title">
<Text>{value}</Text>
</Popover>
)
}

Filter unique option values with Autocomplete component from Material UI

I want Autocomplete from Material UI to list in the drop-down only unique option values from a property of the objects in my list of objects.
In the example below, the drop-down renders a list with the values ['Color1', 'Color2', 'Color1', 'Color2'], however I want it to render only unique values, like ['Color1', 'Color2'].
Here is the list of objects:
options = [
{
id: 1,
name: 'Name1',
color: 'Color1'
},
{
id: 1,
name: 'Name2',
color: 'Color2'
},
{
id: 1,
name: 'Name3',
color: 'Color1'
},
{
id: 1,
name: 'Name4',
color: 'Color2'
},
]
And here is the Autocomplete complete component:
<Autocomplete
freeSolo
value={initialTasting}
options={options}
getOptionLabel={option => option.color}
filterOptions={(options, state) => options}
onChange={(e, value) => {setFieldValue('color', value.color)}}
renderInput={params => (
<TextField
{...params}
label={'Color'}
variant='outlined'
margin='dense'
fullWidth
/>
)}
/>
I left the rest of the code out, since it's too long. It uses Formik, Yup, has children components so on.
I guess I can achieve the result I'm looking for using the prop filterOptions, however, I can't make it. I'm too new to JavaScript..!
Thanks in advance.
What do you think about updating your options array like this?..
function getUniqueListBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()]
}
const optionsUnique = getUniqueListBy(options, 'color');
...and then pass optionsUnique to the options parameter from <Autocomplete /> component

Creating HTML tag in react component

I'm not extending component class, trying to use usestate to manage state. Now I want to add a person component on certain conditions to personList variable inside the method togglePersonsHanler.
I'm expecting a list of HTML tags to be added like
<person name="person1" age=31>
<person name="person2" age=26>
<person name="person3" age=35>
but on console log, I'm getting personList as below
{$$typeof: Symbol(react.element), type: "div", key: null, ref: null, props: {…}, …}$$typeof: Symbol(react.element)type: "div"key: nullref: nullprops: {children: Array(3)}_owner: null_store: {validated: false}_self: null_source: {fileName: "D:\data\react\my-app\src\App.js", lineNumber: 72, columnNumber: 7}
and person tag is not getting added to DOM, any advice, please
import React, { useState } from 'react';
import './App.css';
import Person from './Person/Person';
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{name: "person1", age:31},
{name: "person2", age:26},
{name: "person3", age:25}
],
other: "some Other Value"
} );
const [otherState,setOtherState]=useState({otherState :'some other value'});
const [showPersonsState,setShowPersonsState]=useState({showPersons :false});
let personList=null;
const togglePersonsHanler =() =>{
personList=null;
setShowPersonsState(
{showPersons : !showPersonsState.showPersons}
)
console.log(showPersonsState.showPersons);
if(showPersonsState.showPersons){
personList=(
<div>{personState.persons.map (person =>{
return <person name={person.name} age={person.age}/>
}
)}</div>
);
}
console.log(personList);
}
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button
//onClick={switchNameHandler.bind(this,'Gopu Ravi')}
onClick={togglePersonsHanler}
style={style}> Toggle Person </button>
{ personList }
</div>
);
}
export default App;
You're mapping the object literals by using them as an html tag. You likely meant to use the imported Person component.
<div>
{personState.persons.map (person => (
<Person name={person.name} age={person.age}/>
)}
</div>
And to fix a react-key warning since all mapped elements need unique keys, add a key prop with a value that is unique to the data in the array, like name:
<div>
{personState.persons.map (person => (
<Person key={name} name={person.name} age={person.age}/>
)}
</div>
To correctly toggle the display of the "personList":
Conditionally render the mapped persons array if showPersonsState is true
Simplify showPersonsState state to simply be the boolean value
Use functional state update to correctly toggle showPersonsState from previous state
Updated component code
const App = props => {
const [personState, setPersonState] = useState({
persons: [
{ name: "person1", age: 31 },
{ name: "person2", age: 26 },
{ name: "person3", age: 25 }
],
other: "some Other Value"
});
const [otherState, setOtherState] = useState({
otherState: "some other value"
});
const [showPersonsState, setShowPersonsState] = useState(false);
const togglePersonsHandler = () => setShowPersonsState(show => !show);
return (
<div className="App">
<h1> HI, I'm the react app</h1>
<button onClick={togglePersonsHandler}>Toggle Person</button>
{showPersonsState &&
personState.persons.map(({ age, name }) => (
<Person key={`${name}`} name={name} age={age} />
))}
</div>
);
};

React-Native - I can not use State string as new list element

Yesterday I started learning React Native. Unfortunately I encounter some difficulties right at the beggining.
I was trying to write simple list, which contains some input, and when user clicks "Add new" button, list will extend with new item. When I am trying to use this.state.newName variable in a new list element React is throwing me exception:
" Invariant Violation: Objects are not valid as a React child (found:
object with keys {dispatchConfig, _targetInst, isDefaultPrevented,
isPropagationStopped, _dispatchListeners, _dispatchInstances, type,
target, currentTarget, eventPhase, bubbles, cancelable, timeStamp,
defaultPrevented, isTrusted, nativeEvent}). If you meant to render a
collection of children, use an array instead."
My code is really simple:
export default class HelloWorldApp extends Component {
constructor() {
super();
this.state = {
newName: "",
peopleList: [
{ key: "1", name: "Jonathan", lastname: "Smith" },
{ key: "2", name: "Tomasz", lastname: "Kowalski" },
{ key: "2", name: "Adrian", lastname: "Jakubowski" },
]
}
}
addNew() {
let newName = this.state.newName;
this.setState({
peopleList: [...this.state.peopleList, {key: newName, name: newName, lastname: "Added"}]
});
}
render() {
return (
<View style={styles.peopleList}>
<Text>People I know:</Text>
<TextInput
value={this.state.newName}
onChange={(text) => this.setState({newName: text})}
/>
<Button title="Add new!"
onPress={() => this.addNew()}
/>
<FlatList
data={this.state.peopleList}
renderItem={({ item }) => <Text style={styles.item}>Imie: {item.name} Nazwisko: {item.lastname}</Text>} />
</View>
);
}
}
When I am not using this.state.newName, and just pass some string like:
let newName = "Adam";
It will works just fine. What can be a cause?
Change this:
onChange={(text) => this.setState({newName: text})}
To:
onChangeText={(text) => this.setState({newName: text})}
The right prop is onChangeText
You can see the usage of it in here

How to render an array of objects in React?

could you please tell me how to render a list in react js.
I do like this
https://plnkr.co/edit/X9Ov5roJtTSk9YhqYUdp?p=preview
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
hello
</div>
);
}
}
You can do it in two ways:
First:
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>);
return (
<div>
{listItems }
</div>
);
}
Second: Directly write the map function in the return
render() {
const data =[{"name":"test1"},{"name":"test2"}];
return (
<div>
{data.map(function(d, idx){
return (<li key={idx}>{d.name}</li>)
})}
</div>
);
}
https://facebook.github.io/react/docs/jsx-in-depth.html#javascript-expressions
You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent:
<MyComponent>foo</MyComponent>
<MyComponent>{'foo'}</MyComponent>
This is often useful for rendering a list of JSX expressions of arbitrary length. For example, this renders an HTML list:
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [{name: 'bob'}, {name: 'chris'}],
};
}
render() {
return (
<ul>
{this.state.data.map(d => <li key={d.name}>{d.name}</li>)}
</ul>
);
}
}
ReactDOM.render(
<First />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Shubham's answer explains very well. This answer is addition to it as per to avoid some pitfalls and refactoring to a more readable syntax
Pitfall : There is common misconception in rendering array of objects especially if there is an update or delete action performed on data. Use case would be like deleting an item from table row. Sometimes when row which is expected to be deleted, does not get deleted and instead other row gets deleted.
To avoid this, use key prop in root element which is looped over in JSX tree of .map(). Also adding React's Fragment will avoid adding another element in between of ul and li when rendered via calling method.
state = {
userData: [
{ id: '1', name: 'Joe', user_type: 'Developer' },
{ id: '2', name: 'Hill', user_type: 'Designer' }
]
};
deleteUser = id => {
// delete operation to remove item
};
renderItems = () => {
const data = this.state.userData;
const mapRows = data.map((item, index) => (
<Fragment key={item.id}>
<li>
{/* Passing unique value to 'key' prop, eases process for virtual DOM to remove specific element and update HTML tree */}
<span>Name : {item.name}</span>
<span>User Type: {item.user_type}</span>
<button onClick={() => this.deleteUser(item.id)}>
Delete User
</button>
</li>
</Fragment>
));
return mapRows;
};
render() {
return <ul>{this.renderItems()}</ul>;
}
Important : Decision to use which value should we pass to key prop also matters as common way is to use index parameter provided by .map().
TLDR; But there's a drawback to it and avoid it as much as possible and use any unique id from data which is being iterated such as item.id. There's a good article on this - https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
Try this below code in app.js file, easy to understand
function List({}) {
var nameList = [
{ id: "01", firstname: "Rahul", lastname: "Gulati" },
{ id: "02", firstname: "Ronak", lastname: "Gupta" },
{ id: "03", firstname: "Vaishali", lastname: "Kohli" },
{ id: "04", firstname: "Peter", lastname: "Sharma" }
];
const itemList = nameList.map((item) => (
<li>
{item.firstname} {item.lastname}
</li>
));
return (
<div>
<ol style={{ listStyleType: "none" }}>{itemList}</ol>
</div>
);
}
export default function App() {
return (
<div className="App">
<List />
</div>
);
}
import React from 'react';
class RentalHome extends React.Component{
constructor(){
super();
this.state = {
rentals:[{
_id: 1,
title: "Nice Shahghouse Biryani",
city: "Hyderabad",
category: "condo",
image: "http://via.placeholder.com/350x250",
numOfRooms: 4,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 43
},
{
_id: 2,
title: "Modern apartment in center",
city: "Bangalore",
category: "apartment",
image: "http://via.placeholder.com/350x250",
numOfRooms: 1,
shared: false,
description: "Very nice apartment in center of the city.",
dailyPrice: 11
},
{
_id: 3,
title: "Old house in nature",
city: "Patna",
category: "house",
image: "http://via.placeholder.com/350x250",
numOfRooms: 5,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 23
}]
}
}
render(){
const {rentals} = this.state;
return(
<div className="card-list">
<div className="container">
<h1 className="page-title">Your Home All Around the World</h1>
<div className="row">
{
rentals.map((rental)=>{
return(
<div key={rental._id} className="col-md-3">
<div className="card bwm-card">
<img
className="card-img-top"
src={rental.image}
alt={rental.title} />
<div className="card-body">
<h6 className="card-subtitle mb-0 text-muted">
{rental.shared} {rental.category} {rental.city}
</h6>
<h5 className="card-title big-font">
{rental.title}
</h5>
<p className="card-text">
${rental.dailyPrice} per Night · Free Cancelation
</p>
</div>
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
}
export default RentalHome;
Try this:
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
{listItems}
</div>
);
}
}

Categories

Resources