So this is my props if I console.log(this.props)
list:Array(1):
{user: "Jack Nicholson", userid: "5b684ed8d3eb1972b6e04d32", socket: "1c0-Jb-kxe6kzPbPAAAD"}
However when I map through my list and use component <UserItem user={user.user} />; My UserItem keeps returning undefined.
render() {
const UserItem = user => (
<li>
<div className="btn-group dropup">
<button
type="button"
className="btn btn-secondary dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{user}
{console.log(user)}
</button>
<div className="dropdown-menu" />
</div>
</li>
);
return (
<ul>
{this.props.list.map((user, i) => {
console.log(this.props);
<UserItem user={user.user} />;
})}
</ul>
);
}
Arrow function in JS come in two forms: "concise body" and "block body". In concise form, as in your const UserItem = user => ( function, the provided expression is implicitly returned.
However, in block form, as in your this.props.list.map((user, i) => { function, you must use an explicit return statement.
Try adding one to your code:
{this.props.list.map((user, i) => {
console.log(this.props);
return <UserItem user={user.user} />;
})}
You could simply use round brackets instead of curly braces;
return (
<ul>
{
this.props.list.map((user, i) => (
<UserItem user={user.user} />
))
}
</ul>
);
If you are getting the list as API response then handle the initial rendering also.However,return your UserItem component in the map function.try this code
return (
<ul>
{this.props.list && this.props.list.map((user, i) => {
console.log(this.props);
return <UserItem user={user.user} />;
})}
</ul>
);
You need to add a return statement before <UserItem user={user.user} /> ,also make sure to check if this.props.list exists before rendering it.
like so:
{this.props.list? this.props.list.map((user, i) => {
console.log(this.props);
return <UserItem user={user.user} />;
}):<span/>}
Related
I want to pass emailID as the second parameter to . Can you help me to understand how to pass additional parameter in Curly braces as a function parameter and how to access it in AccountMenuSidebar.
Sorry for asking this basic question.I am a newbie to Javascript and React.
class Invoices extends Component {
render() {
var emailID="guest#somedomain.com";
const accountLinks = [
{
text: 'Account Information',
url: '/account/user-information',
icon: 'icon-user',
},
{
text: 'Notifications',
url: '/account/notifications',
icon: 'icon-alarm-ringing',
},
];
return (
<section className="ps-my-account ps-page--account">
<div className="container">
<div className="row">
<div className="col-lg-4">
<div className="ps-page__left">
<AccountMenuSidebar data={accountLinks} /> // Want to pass email id as second argument here
</div>
</div>
</div>
</div>
</section>
);
}
}
export default Invoices;
const Accountbar = ({ data }) => (
<aside className="ps-widget--account-dashboard">
<p>{email}</p>
<div className="ps-widget__content">
<ul>
{data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
</div>
</aside>
);
export default Accountbar;
<AccountMenuSidebar data={accountLinks} email={emailID} />
and
const Accountbar = (data , emaildID) => (...
or
const Accountbar = (props) => (...
and then you can use props like this...
<ul>
{props.data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
When you pass the props from Invoices, you usually acces them like this in AccountMenuSidebar:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = (props) => {
return (
<p>{props.data}</p>
)
}
However, using destructuring, which lets you directly unpack variables from an object in JavaScript, you can access the props like this instead:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = ({ data }) => {
return (
<p>{data}</p>
)
}
So if you want to send another prop, you can access it the same way, i.e.
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = (props) => {
return (
<>
<p>{props.data}</p>
<p>{props.email}</p>
</>
)
}
or using destructuring:
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = ({ data, email }) => {
return (
<>
<p>{data}</p>
<p>{email}</p>
</>
)
}
I have 3 components.
In ListCard.js, I map cards array and based on the card the user click on, I call handleChangeCardData to update the modal's text.
My question is: How do I update/change the modal's text when my handleChangeCardData function is inside ListCard.js and my modal is on the same level. (Both are in Board.js)
Board.js
const [cardTitle, setCardTitle] = useState("");
return (
{columns.map((column, index) => (
<div className="column__container" key={index}>
<div className="column__header">
<div className="columnHeader__name">
<p>{column.name ? column.name : "..."}</p>
</div>
<div className="columnHeader__button">
<button
className="btn btn-sm --create-card-btn"
data-bs-toggle="modal"
data-bs-target="#modal-card"
onClick={() => setColumnId(column.id)}
>
New item
</button>
</div>
</div>
<Droppable droppableId={column.id}>
{(provided, snapshot) => (
<div
className="column"
ref={provided.innerRef}
{...provided.droppableProps}
>
<ListCard columnId={column.id} />
{provided.placeholder}
</div>
)}
</Droppable>
</div>
))}
<ViewCardModal cardTitle={cardTitle} />
)
LisCard.js
const handleChangeCardData = (cardTitle) => {
setCardTitle(cardTitle);
}
return (
{cards.map((card, index) => (
<>
<div key={index}>
<Draggable draggableId={card.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div
className="card --listcard-card"
onClick={() => handleChangeCardData(card.title)}
data-bs-toggle="modal"
data-bs-target="#modal-card-details"
style={{ border: `2px solid ${card.color}` }}
>
<div className="card-body">
<p>{card.title}</p>
</div>
</div>
</div>
)}
</Draggable>
</div>
</>
))}
)
ViewCardModal.js
function ViewCardModal(props) {
return (
<div>{props.cardTitle}</div>
)
}
In general, lift state up. In this case, it sounds like that means moving the state into Board and then passing that state to whatever child components need it (as a prop), and the state setter to whatever (other) child components need it.
Here's a minimal example of lifting state up. I haven't tried to recreate the full complexity of your example, just to provide an example of Parent having state that ChildA uses and ChildB sets:
const {useState} = React;
const ChildA = React.memo(({counter}) => {
console.log("ChildA rendered");
return <div>Counter = {counter}</div>;
});
const ChildB = React.memo(({setCounter}) => {
console.log("ChildB rendered");
return <input
type="button"
value="Increment Counter"
onClick={() => setCounter(c => c + 1)}
/>;
});
const Parent = () => {
const [counter, setCounter] = useState(0);
return (
<div>
<ChildA counter={counter} />
<ChildB setCounter={setCounter} />
</div>
);
};
ReactDOM.render(<Parent />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"></script>
If there are several levels of hierarchy between where the state is being held and a descendant component that needs it, you might use context instead of props (although you might also look at component composition instead). See those links for details.
you cant do that directly, but must use props.
in list:
onClick={() => props.onClick(card.title)}
in board:
handleChangeCardData = (cardTitle) => {
setCardTitle(cardTitle);
}
<ListCard columnId={column.id} onClick={(e)=>handleChangeCardData(e)}/>
Inside ListCard:
const ListCard = ({setCardTitle}) => {...}
onClick={() => setCardTitle(card.title)}
In the parent:
<ListCard columnId={column.id} setCardTitle={setCardTitle} />
I'm trying to make a filter for my restaurantsAll. The code works without the filter but when I try to add the filter I get this error: TypeError: restaurant.toLowerCase is not a function | Line 25
When I use a:
const list = ['jef', 'uwmoeder'] and list.map
it does work but with the store. it doesn't
Can someone take a look at it please?
<ul>
{store.restaurants.map(restaurant => {
if (filter.lenght !== 0) {
if (restaurant.toLowerCase().startsWith(filter.toLowerCase())) {
return (
<li>
<Restaurant
restaurant={restaurant}
key={restaurant.id}
onClick={() => {
uiStore.setCurrentRestaurant(restaurant);
uiStore.setCurrentView(VIEWS.detail);
}}
/>
</li>
);
} else {
return null;
}
}
return (
<li>
<Restaurant
restaurant={restaurant}
key={restaurant.id}
onClick={() => {
uiStore.setCurrentRestaurant(restaurant);
uiStore.setCurrentView(VIEWS.detail);
}}
/>
</li>
);
})}
</ul>
this is the component Restaurant:
const Restaurant = ({restaurant, onClick}) => {
return useObserver(() => (
<div className={style.restaurantlist} >
<Link to={`/detail/${restaurant.id}`}>
<img onClick={onClick} className={style.restaurantfoto} src={restaurant.picture} alt={restaurant.name} />
</Link>
<section className={style.information}>
<Information restaurant={restaurant} />
</section>
</div>
));
}
I couldn't understand why...here is the GitHub repository: https://github.com/Dronrom/React-test
That’s because you initialized peopleList as null in your component. So map works only on arrays so you need to check peopleList whether its really an array before doing map on it so
Change
renderItems(arr) {
return arr.map(({id, name}) => {
return (
<li className="list-group-item"
key={id}
onClick={() => this.props.onItemSelected(id)}>
{name}
</li>
);
});
}
To
renderItems(arr) {
if(arr){
return arr.map(({id, name}) => {
return (
<li className="list-group-item"
key={id}
onClick={() => this.props.onItemSelected(id)}>
{name}
</li>
);
});
}
}
I think your issue may be that react renders once before componentDidMount(). This is an issue because your calling map on arr which is null. const { peopleList } = this.state; you set people list to your current state which you set as default to be null, state = {peopleList: null}; then you later call this.renderItems(peopleList); which people list is still null at this moment so you are getting the Cannot read property 'map' of null error.
I belive something like componentWillMount is what you need instead. I recommend looking at this post which has a similar issue of react life cycle methods. React render() is being called before componentDidMount()
the answer is very simple: the type of the input isn't array type, it might be null or undefined. so that it doesn't have .map function.
How to fix:
Make sure your input must be array type before call renderItems().
render(){
const { peopleList } = this.state;
const items = (peopleList && peopleList.length) ? this.renderItems(peopleList) : null;
return(
<ul className="item-list list-group">
{items}
</ul>
);
}
Or:
Make sure your input must be array type before do mapping:
renderItems(arr) {
return !arr ? null : arr.map(({id, name}) => {
return (
<li className="list-group-item"
key={id}
onClick={() => this.props.onItemSelected(id)}>
{name}
</li>
);
});
{product.size?.map(c=>(
<FilterSizeOption key={c}>{c}</FilterSizeOption>
))}
Wrapping the return statement with a if statement worked for me
So changed
return (
<div>
<Navbar />
{countries.map((country, i) => {
return (
<div>
<span key={`${country.name.common}${i}`}>
{country.name.common}
</span>
</div>
);
})}
</div>
);
to this
if (countries) {
return (
<div>
<Navbar />
{countries.map((country, i) => {
return (
<div>
<span key={`${country.name.common}${i}`}>
{country.name.common}
</span>
</div>
);
})}
</div>
);
}
I have the following directory structure:
Todo -> TodoList -> TodoItem
I pass todos, onSelection, onDeletion function from todo towards the list and so on to the item.
Both the list and item components are stateless, the issue is that, when I click the item, it fires onClick() as well as onSelect() method too due to bubbling, I don't want to use event.stopProppogation in any case.
I've also tried the method in stateless component as following but it fires up when the component is loaded.
TodoItem:
const TodoItem = ({text, onSelection, onDeletion, id}) => {
const wrapDeletion = (id) => {
onDeletion(id);
}
return (
<li className="list-group-item" onClick={onSelection.bind(this, id)}>
{text}
<button className="btn btn-danger float-right" onClick={wrapDeletion(id)}>
{BTN_ACTIONS.DELETE}
</button>
</li>
);
}
Todo List:
<ul className="list-group">
{todos.map(todo =>
<TodoItem
key={todo.id}
id={todo.id}
onSelection={onSelection}
text={todo.text}
onDeletion={onDeletion}/>)
}
</ul>
Todo:
<TodoList
todos={this.state.todos}
onSelection={onSelection}
onDeletion={onDeletion}
/>
you should intercept the click event and stop it from propagation. To do so, wrap a div and apply it the stopPropagation like so
const TodoItem = ({text, onSelection, onDeletion, id}) => {
const wrapDeletion = (id) => {
onDeletion(id);
}
return (
<li className="list-group-item" onClick={onSelection.bind(this, id)}>
{text}
<div className="clickinterceptor" onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}>
<button className="btn btn-danger float-right" onClick={wrapDeletion(id)}>
{BTN_ACTIONS.DELETE}
</button>
</div>
</li>
);
}
The docs for this are here https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
Make sure you aren’t calling the function when you pass it to the
component
You are giving a STRAIGHT CALL to a function, while passing it as a all back. Following is one of the solution, passing the callback as a "function returning the call".
const TodoItem = ({text, onSelection, onDeletion, id}) => {
const wrapDeletion = (id) => {
onDeletion(id);
}
return (
<li className="list-group-item" onClick={onSelection.bind(this, id)}>
{text}
<button className="btn btn-danger float-right" onClick={() => wrapDeletion(id)}>
{BTN_ACTIONS.DELETE}
</button>
</li>
);
}
You can further read it here!
https://reactjs.org/docs/faq-functions.html#how-do-i-pass-a-parameter-to-an-event-handler-or-callback