Passing a function through props - javascript

i have a function that selects a row of a table, this function is below, it is within my component of SupplierSearchComponent
const handleAddressCheck = item => {
const itemIndex = supplierAddress.findIndex(supplierItem => supplierItem.id === item.id);
let newAddresses;
if (itemIndex >= 0) {
//the reasoning behind using a filter is that splice will not return an updated array, filter however does
newAddresses = supplierAddress.filter(x => x.id !== item.id);
} else {
newAddresses = [...supplierAddress, item];
}
setSupplierAddress(newAddresses);
props.onSelectAddress(newAddresses);
console.log('this is the item => ', newAddresses);
};
However i also pass this function into another component as
const onSelectedAddressesChange = (addresses) => {
props.setFieldValue('supplierAddresses', addresses);
};
I then pass the value of this into another component as
<SearchSuppliers
name="supplierSearch"
label="Supplier Search"
id="supplierSearch"
onSelectAddress={onSelectedAddressesChange}
/>
then from search suppliers i pass the prop into the original component of
<SupplierSearchComponent
onSelectAddress={props.onSelectAddress}
/>
Now finally within my SupplierSearchComponent i should be able to pass this prop as
<SupplierContactDetails
onSelectAddress{props.onSelectAddress}
/>
However within my ContactDetails i cannot actually console.log(onSelectAddress) as it will result as undefined, how could i successfully pass this function or rather its result through?

I suppose you skip "=" when passed props at last component "SupplierContactDetails".

Related

Looping through an array to get the data for specific

I want to loop through an array of objects to get a specific id and then render its data in a component in react, why it cannot be mapped, what is wrong here?
const projectData = projects.find(element => {
return element.id === projectId;
});
return (
{projectData.map(project => {
return <ProjectData key={project.id} {...project}></ProjectData>;
})}
)
find is returning either null or an object. you need an array to loop over the map. use the filter operator instead
const projectData = projects.filter(element => {
return element.id === projected;
});
The find() method returns the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
So you don't need to use map for rendering.
const projectData = projects.find(element => {
return element.id === projectId;
});
return (
{projectData && <ProjectData key={projectData.id} {...projectData}></ProjectData>}
)
If there are many elements that satisfies the testing function please use the filter method, not find method.
In this case, the return value is the array, so you should use map for rendering.
const projectData = projects.filter(element => {
return element.id === projectId;
});
return (
{projectData.map(project => {
return <ProjectData key={project.id} {...project}></ProjectData>;
})}
)

destructuring props in component getting different result

New to react world, trying to learn destructuring, have been reading about it but stuck here,
if i do it like this function MList({action}) { // const data = [action];} i am just getting 'cameras'. So how to destructure and get same result as with props below
this is Mcard.js:
<Box pt={1}>
<MList
action="cameras"
/>
</Box>
This is inside MList komponent:
i want to destructure this code ( works gives 'name' and 'ident'):
function MList(props) {
const initialize = () => {
const data = props[props.action];
if (!data || data.length < 1) {
return;
}
data.map((e) => {
collapseStates["" + e.name + e.ident] = false;
return;
});
setCollapseS(collapseS);
};
}
I don't know React but destructuring the arguments should be something like the following
function MList({action, ...tail}) {
const initialize = () => {
const data = tail[action];
if (!data || data.length < 1) {
return;
}
data.map(({name, ident}) => {
collapseStates["" + name + ident] = false;
return;
});
setCollapseS(collapseS);
};
}
Also I would suggest using data.forEach instead of data.map if you don't need to save the result in another array
Nikita is correct about using props["action"] or props.action to grab the values. But you can actually destructure the props right inside of the function declaration like so:
function MList({ action, ...other props }) {
// Can now use action directly instead of props.action
}
It is also worth noting that array and object destructuring is not react specific, this is just how javascript works.
Edit: Accessing the action variable from here will give you "cameras" as a result because that is what you passed into the props

React: setState doesn't re-render the component

I'm implementing a shopping cart for a ecommerce website. The shopping cart is a state variable shopCart represented by an array of objects. Each object contains information about a product, such as title and price. I am trying to implement a remove button, which is actually doing what is intended from it, which is to remove items from the shopCart state, but the changes are not represented on the screen render. I can empty the cart, but the screen still shows the products. Here is the main code of the shopping cart page:
return (
<div class={styles.container}>
<h1>Product</h1><h1>Quantity</h1><h1>Unit price</h1><h1>Total price</h1><div></div>
{
shopCart.map((product, i, array) => <CartItem key={product.id} product={product} index={i} array={array}/>)
}
</div>
)
And here is the implementation of CartItem.js
const CartItem = (props) => {
let { shopCart, setShopCart } = useContext(Context);
let product = props.product;
// takes the identification of a shopping cart product and removes it from the cart
const decrease = (element) => {
shopCart.forEach((el, i) => {
if (el.hasOwnProperty('id')) {
if (el.id === element) {
let aux = shopCart;
aux.splice(i, 1);
setShopCart(aux);
}
}
})
}
return (
<div>
<img src={product.image}></img>
<h1>{product.quantity}</h1>
<h1>{product.price}</h1>
<h1>{product.price * product.quantity}</h1>
<button onClick={() => {
decrease(product.id);
}}>Remove</button>
</div>
)
}
Why isn't it rendering the cart correctly, even though the cart items are being removed after each click of the remove button ?
Issue
You are mutating state. You save a reference to state, mutate it, then save it back into state, so the array reference never changes. React uses shallow equality when checking if state or props update.
const decrease = (element) => {
shopCart.forEach((el, i) => {
if (el.hasOwnProperty('id')) {
if (el.id === element) {
let aux = shopCart; // <-- Saved state ref
aux.splice(i, 1); // <-- mutation
setShopCart(aux); // <-- Saved ref back to state
}
}
})
}
Solution
The correct way to update arrays in react state is to copy the array elements into a new array reference. This can be easily accomplished by filtering the current cart by item id. I also suggest changing the argument name so it is clearer what it represents.
const decrease = (id) => {
setShopCart(shopCart => shopCart.filter(item => item.id !== id));
}
You're modifying the shopCart (aux is a reference) directly which is both the context and the collection that you're iterating over. You need to make sure you're updating a copy of the shopping cart and resetting the context. Minimally, you can do the following:
const decrease = (element) => {
shopCart.forEach((el, i) => {
if (el.hasOwnProperty('id')) {
if (el.id === element) {
let aux = shopCart.slice(); // makes a copy
aux.splice(i, 1);
setShopCart(aux);
}
}
})
}
However, I suggest using the approach Drew recommended. The current approach isn't ideal.
The solution is much simpler than you think. You can use array.filter to remove the matching product by id.
const CartItem = (props) => {
const { product} = props;
let { shopCart, setShopCart } = useContext(Context);
// takes the identification of a shopping cart product and removes it from the cart
const handleClick = (e) => {
const filteredShopCart = shopCart.filter(item => item.id !== product.id);
setShopCart(filteredShopCart);
};
return (
<div>
<img src={product.image}></img>
<h1>{product.quantity}</h1>
<h1>{product.price}</h1>
<h1>{product.price * product.quantity}</h1>
<button onClick={handleClick}>Remove</button>
</div>
);
};

View not updating on state change

So I got a list of buttons that looks like this
The functionality that I aim for is when you press a button its background will change to another color.
const getUpdatedSelectedItemsArray = (selectedItems, id) => {
selectedItems = []
selectedItems.push(id);
return selectedItems;
};
I use this function to return a list of selected items. Currently I'm only returning one item but I made it an array so I can handle multiple items in the future.
In the render function I have something like this:
<View style={feed_back_page_styles.buttons_wrapper}>
{
feedbackButtons.map((item, i) => (
<TouchableOpacity style={this.state.selectedItems.includes(item.key)?feed_back_page_styles.pressedStyle:feed_back_page_styles.inputStyle}
onPress={()=>this.onButtonPress(item.key)}>
<Text style={this.state.selectedItems.includes(item.key)?feed_back_page_styles.option_text_style_pressed:feed_back_page_styles.option_text_style}>{item.data}</Text>
</TouchableOpacity>
))
}
</View>
feedbackButtons is just an array with a key and text.
The onButtonPress method looks like this:
onButtonPress = (key) =>{
updatedItems = getUpdatedSelectedItemsArray(this.state.selectedItems,key);
this.setState({selectedItems:updatedItems},()=>console.log(this.state.selectedItems));
console.log("Do smth else here");
}
The problem is that the view does not update on state change. When I click the button the state gets updated but the view stays the same.
I think this is wrong
const getUpdatedSelectedItemsArray = (selectedItems, id) => {
selectedItems = []
selectedItems.push(id);
return selectedItems;
};
Since you are passing the this.state.selectedItems as 1st argument from your onButtonPress, actually its not creating new array, but using the same reference of state and state should not be modified directly, always use setState().
So basically what you are doing is :
const getUpdatedSelectedItemsArray = (id) => {
this.state.selectedItems = []
this.state.selectedItems.push(id);
return selectedItems;
};
Which is completely wrong and might be the actual issue.
what you can do instead is :
const getUpdatedSelectedItemsArray = (selectedItems=[], id) => {
const items = [...selectedItems]
items.push(id);
return items;
};
and then :
onButtonPress = (key) =>{
const updatedItems = getUpdatedSelectedItemsArray(key); // since currently you want to keep only 1 item in the list
/* Incase more than 1 items, you can then use this
const updatedItems = getUpdatedSelectedItemsArray(this.state.selectedItems, key);
*/
this.setState({selectedItems:updatedItems},()=>console.log(this.state.selectedItems));
console.log("Do smth else here");
}
Hope this resolves your issue.
Also, if you can share your component, it can help if there is some other issue with your component like if you are using PureComponent.

Grabbing value onCheck from Checkbox/ListItem

Let's say I have the following rendered by a React.Component implementing material-ui components:
{data.map(value => (
<ListItem
key={data.indexOf(value)}
primaryText={value}
leftCheckbox={
<Checkbox
onCheck={this.props.handleAddOption}>
</Checkbox>}>
</ListItem>
When the Checkbox is chosen, I want to push the value into the array in state
handleAddOption = (value) => {
this.setState((....))
}
How do I go about doing that?
UPDATE
found the solution here Passing a function with parameters through props on reactjs
You need to pass the value from the CheckBox component to the function prop. You can do this by:
<ListItem
key={data.indexOf(value)}
primaryText={value}
leftCheckbox={
<Checkbox
onCheck={(e, isChecked) => this.props.handleAddOption(value, isChecked)}>
</Checkbox>}>
</ListItem>
And for your handler:
handleAddOption(value, isChecked) {
this.setState((prevState, props) => {
// Get the old state's value, sticking with immutable pattern
let yourProperty = prevState.yourProperty;
// Determine if the value already exists in your property's array
const exists = yourProperty.find(v => v === value);
if (isChecked) {
// If the checkbox is checked...
// If the property exists, don't do anything
// If it isn't there, add it
!exists && yourProperty.push(value);
} else {
// If the checkbox is NOT checked...
// If the property exists, filter the array to remove it
// If it isn't there, do nothing
exists && (yourProperty = yourProperty.filter(v => v !== value));
}
// Return the new state
return { yourProperty };
});
}
UPDATE
I've updated the solution a bit with documentation and a couple of typos, and created a working example on CodeSandBox here: https://codesandbox.io/s/pj0m4w3qp7

Categories