How to pass two functions to onClick event in react - javascript

I want to pass two functions to onClick event which is handleSubmit and handleDelete to the HomePage.js from the HomeItem.js
Here is my Error:
No duplicate props allowed react/jsx-no-duplicate-props.
Here is my HomePage.js:
const HomePage = props => {
const tvshow = props.item;
let res;
if (tvshow.length > 0) {
res = tvshow.map(res=> (
<Content item={res} onClick={props.onClick}/>
));
}
return (
<div>
<Container>
<Row>{res}</Row>
</Container>
</div>
);
};
export default HomePage;
Here is my HomeItem.js:
const HomeItem = props => {
function handleSubmit() {
props.onClick({
name: props.item.name,
id: props.item.id
});
}
function handleName() {
props.onClick({
name: props.item.name
});
}
<Button onClick={handleSubmit}></Button>
<Button onClick={handleName}></Button>
Here is my App.js:
handleSubmit(newFavorite) {}
handleName(newFavorite) {}
render() {
<Route
exact
path="/"
render={() => (
<HomePage
item={this.state.SaveFavorite}
onClick={this.handleSubmit}
onClick={this.handleName}
/>
)}
/>
}
So my question is how to put 2 onClick function to the Hompage.js

How about this:
<HomePage
item={this.state.SaveFavorite}
onClick={(favorite)=>{
this.handleSubmit(favorite);
this.handleName(favorite);
}
}
/>
This assumes your goal is to call both functions one at a time. If they should be called in different situations give one function a different name, eg onSubmit or onNameChange.

Try This:
<HomePage
item={this.state.SaveFavorite}
onClick={(newFavorite) => this.handleSubmit(newFavorite);this.handleName(newFavorite)}
/>

you can pass multiple functions to events in react, let say changeEvent, to do follow those steps.
1- create your function two or the number of function you like.
2- create an object that contains those functions
3- pass the object as a props to where it would be consumed
4- choose the correspondant function to each form or whatever you need.
here is an example, this sample is with typescript.
const onChangeFunctions = {
onChangeForm1: handleChangeForm1,
onChangeForm2: handleChangeForm2,
};
<MainForm
onChange={onChangeFunctions} // here is your function
datas={yourData}
otherProps={otherProps}
/>
Now you use the fucntion on the child components
interface PropsFrom {
model1: Model1;
model2: Model2;
onChange: any;
}
export default function ProductForm(props: PropsForm) {
return (
<Container maxWidth="lg">
<Grid item md={6}>
<FormOne
model={props.model1} // the model that bind your form
onChange={props.onChange.onChangeForm1} // here you can use your first function
/>
</Grid>
<Grid item md={6}>
<FormTwo
model={props.model2} // the model that bind your form
onChange={props.onChange.onChangeForm2} // here you can use your second function
/>
</Grid>
</Grid>
</Container>
for javascript just pass the functions as props and delete the interface from the child components.

Related

How to use useState in a child Component

I'm not sure why I can't use my pass on useState from Parent component <AppMain /> to the child component <GroupPage /> and <CreateGroupForm />
What I'm trying to do:
I'm working on an update functionality, where on clicking the edit button in <GroupPage />, I want the content of GroupPage to fill on the form fields of <CreateGroupForm />. So for that, I have created states currentId and setCurrentId in <AppMain /> since it's the parent component of both, and I can pass on these to its child components assuming they both share the states.
const AppMain = () => {
const [ currentId, setCurrentId ] = useState(null)
return (
<div>
<Switch>
<Route path="/groupMain" exact> <GroupMain /> </Route>
<Route path="/groupMain/:id" exact> <GroupPage setCurrentId={setCurrentId} /> </Route>
<Route path="/createGroup" exact> <CreateGroupForm currentId={currentId} setCurrentId={setCurrentId} /> </Route>
</Switch>
</div>
)
}
export default AppMain
const GroupPage = ({setCurrentId}) => {
const { group } = useSelector((state) => state.groups)
// the reason for this condition is to prevent rendering something before data is actually fetched
if(!group) return null
return (
<div>
<EditButton onClick= {() => {
setCurrentId(group._id)
history.push(`/createGroup/`)
}} />
<h1>{group.groupName}</h1>
</div>
)
}
export default GroupPage
Now when clicking on the edit button of <GroupPage /> I'm setting the current group Id in setCurrentId and directing it to the <CreateGroupForm />. In <CreateGroupForm /> I'm checking if currentId matches the one with the already existed group. And by useEffect I'm populating those values in form fields.
const CreateGroupForm = ({currentId, setCurrentId}) => {
const [groupData, setGroupData] = useState({
groupName: ''
})
const group = useSelector((state) => currentId ? state.groups.groups.find((grp) => grp._id === currentId) : null)
console.log(group) // null
console.log(currentId) // undefined
useEffect(() => {
if(group) setGroupData(group)
}, [group])
return (
<div>
<MainForm>
<form autoComplete="off" onSubmit={handleSubmit}>
<h1>{ currentId ? 'Editing' : 'Creating'} a Group:</h1>
<label htmlFor="Group Name">Your Group Namee: </label>
<input type="text" value={groupData.groupName} onChange={(e) => setGroupData({ ...groupData, groupName: e.target.value })} />
<button type="submit">Submit</button>
</form>
</MainForm>
</div>
)
}
export default CreateGroupForm
What is happening:
On clicking the Edit button, the form fields are not populating with the group content.
Please any help would be appreciated.
It's not a good practice to pass setStates between its children. I recommend you to create callback functions in your appMain, and pass that functions to your GroupPage and CreateGroupForm components. When you call those functions inside the components your functions will change your currentIdState in the appMain. Changing the state of currentId in your appMain the components will recive the new state of currentId

How to make data accessible to subchild component using React useContext [duplicate]

I have a component that renders:
<View>
<SomeContext.Provider value={state}>
{props.children}
</SomeContext.Provider>
</View>
I don't understand how to create a method accessible in the following way:
<View>
<SomeContext.Provider>
{(method) =>
<Button
title={"Magic"}
onPress={() => {
method()
}}
></Button>
}
</SomeContext.Provider>
</View>
Consumer Component
You need to consume a context through a Context.Consumer that is a direct descendent of the the relevant Provider. The docs have decent examples, though they mix class-based and functional components. Updating Context from a Nested Component
Sandbox: https://codesandbox.io/s/react-context-consumer-passing-method-to-nested-child-forked-u0bi8
const initData = {
data: { a: "Text from Context", b: "2" },
method: () => {
console.log("Method called.");
}
};
const SomeContext = React.createContext();
export default function App() {
return (
<SomeContext.Provider value={initData}>
<Content />
</SomeContext.Provider>
);
}
function Content() {
return (
<div>
<SomeButton />
</div>
);
}
function SomeButton() {
return (
<SomeContext.Consumer>
{({ data, method }) => <button onClick={method}>{data.a}</button>}
</SomeContext.Consumer>
);
}
useContext Hook
The useContext hook is also available, and possibly provides a more familiar pattern. The Consumer of the context still needs to be a descendant of the Provider, but it is accessed via an hook rather than through a Consumer component.
Sandbox: https://codesandbox.io/s/react-context-passing-method-to-nested-child-1fcno
const initData = {
data: { a: "Text from Context", b: "2" },
method: () => {
console.log("Method called.");
}
};
const SomeContext = React.createContext();
export default function App() {
return (
<SomeContext.Provider value={initData}>
<Toolbar />
</SomeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<SomeButton />
</div>
);
}
function SomeButton() {
const { data, method } = React.useContext(SomeContext);
return <button onClick={method}>{data.a}</button>;
}
The Context counsumer docs actually tells you everything you need to know:
A React component that subscribes to context changes. This lets you subscribe to a context within a function component.
Requires a function as a child. The function receives the current context value and returns a React node. The value argument passed to the function will be equal to the value prop of the closest Provider for this context above in the tree. If there is no Provider for this context above, the value argument will be equal to the defaultValue that was passed to createContext().
So, in your example you need to pass the method you want to the provider:
const method = () => {};
<View>
<SomeContext.Provider value={{ method }}>
{props.children}
</SomeContext.Provider>
</View>
And then in the Consumer you can call it like this:
<View>
<SomeContext.Consumer>
// using destructuring here,
// so its ({ method }) instead of (method)
{({ method }) =>
<Button
title={"Magic"}
onPress={() => method()} />
}
</SomeContext.Consumer>
</View>
Also, it's important that the consumer component is inside the provider.

React Component dynamic item renderer

I have a list component that renders data from an array and I like to pass the component as property to render each one of the items. Does anyone knows how I can pass a class as a property to my list component and use it to dynamically render the items.
JSX is transformed by babel so every <Component {...props} /> becomes React.createElement('Component', props)
so you can do this
const items = [{
constr: 'Child',
someProp: 'value'
}];
function Parent(props) {
return (<div>
{props.items.map(item =>
React.createElement(item.constr, item)
)}
</div>);
}
<Parent items={items} />
edit:
you can also pass the reference of the class/function
const items = [{
constr: Child, // <- reference
someProp: 'value'
}]
and then take advantage of the object destructing assignment
function Parent(props) {
return (<div>
{props.items.map(({constr: Element, ...props}) =>
<Element {...props} />
)}
</div>);
}
You want a reusable higher order component for that. Something like this:
// -> Build your HOC
function WithItem(Component) {
return function WithItemComponent({ item, ...props }) {
if (item) return (<Component {...props}>{item}</Component>);
return (<Component {...props}/>);
}
}
export default WithItem;
// -> Then Implement it
const WithItemMyComponent = WithItem(MyComponent)
render() {
return (
<WithItemMyComponent item={item} />
)
}

React - Can't get function to fire from child props

I am trying to get this function to fire from on click call in a child component.
getTotalOfItems = () => {
console.log('anything at all?')
if (this.props.cart === undefined || this.props.cart.length == 0) {
return 0
} else {
const items = this.props.cart
var totalPrice = items.reduce(function (accumulator, item) {
return accumulator + item.price;
}, 0);
this.setState({
estimatedTotal: totalPrice
});
};
}
This on click is being fired from within a Cart component
<button onClick={() => {props.addToCart(item); props.getPrice.bind(this)} }>+</button>
The cart component is being added to the ItemDetails component here
export default class ItemDetails extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
render() {
return(
<div>
<Button
className="item-details-button"
bsStyle="link"
onClick={() => this.setState({open: !this.state.open})}
>
{this.state.open === false ? `See` : `Hide`} item details
{this.state.open === false ? ` +` : ` -`}
</Button>
<Collapse in={this.state.open}>
<Cart
getPrice={this.props.getPrice}
/>
</Collapse>
</div>
)
}
}
Finally the ItemDetails component is added into the app.js like so
render() {
return (
<div className="container">
<Col md={9} className="items">
<ProductListing products={this.props.initialitems} />
</Col>
<Col md={3} className="purchase-card">
<SubTotal price={this.state.total.toFixed(2)} />
<hr />
<EstimatedTotal
price={this.state.estimatedTotal.toFixed(2)} />
<ItemDetails
price={this.state.estimatedTotal.toFixed(2)}
getPrice={ () => this.getTotalOfItems() }
/>
<hr />
<PromoCodeDiscount
giveDiscount={ () => this.giveDiscountHandler() }
isDisabled={this.state.disablePromoButton}
/>
</Col>
</div>
);
};
If I remove the () = > before the this.getTotalOfItems() it fires the function on the onClick, however it causes an infinite loop of re-rendering out the app causing an error.
Is there anyway to fix this? I am a novice at React and this is one of my first projects using it. Any advice shall be appreciated.
Sorry if this isn't explained to well, I am happy to provide any additional information if required.
Thanks!
You have to trigger getPrice method, now all you do is binding this context. Instead of props.getPrice.bind(this) you should have: props.getPrice()
props.getPrice.bind(this) doesn't call the function it just binds 'this' to it.
You should use props.getPrice() instead, also you don't have to bind the context of a children to it.
Some additionnal tips/explanations :
You can rewrite all your functions calls like this one :
getPrice={ () => this.getTotalOfItems() }
to
getPrice={this.getTotalOfItems}
It will pass the function to the child instead of creating a function which trigger the function (same result, better performance)
But if you do this :
getPrice={this.getTotalOfItems()}
It'll trigger the function at each render(), causing an infinite loop if the function triggers a render() itself by calling this.setState()

React: How to supply callback arrow functions to stateless functional child components?

I'm struggling to supply a callback function to a stateless functional child component. I have these two components:
export const FrontDeskGUI = () => {
const callback = (event) => console.log(event.target.value);
return (
<Col xs={12}>
<Panel collapsible defaultExpanded header="Empfang">
<ButtonGrid/>
</Panel>
<Panel collapsible defaultExpanded header="Verwaltung">
<ButtonGrid/>
</Panel>
<CommandInput callback={callback}/>
</Col>);
};
export const CommandInput = (callback) => {
const style = {
textTransform: 'uppercase'
};
return (
<Col xs={12}>
<form>
<FormGroup
controlId="command">
<FormControl
type="text"
style={style}
placeholder="Kürzel eingeben"
onChange={callback}/>
</FormGroup>
</form>
</Col>);
};
During rendering I get the following error:
Warning: Failed form propType: Invalid prop onChange of type object supplied to input, expected function. Check the render method of FormControl.
Everytime I input something into the text input, I get the following error:
Uncaught TypeError: inputProps.onChange.call is not a function
at Object.executeOnChange (LinkedValueUtils.js:132)
Is this at all possible in a stateless environment? Technically the supplied callback function is constant, so there is no inherent state in the CommandInput component. I've seen some answers messing around with binding functions to the correct this pointer but I'd like to avoid that if possible.
The single argument your SFC receives is a properties object with a property for each property used on the component in the JSX markup.
So either accept the property and use its callback property:
export const CommandInput = (props) => {
// ...use props.callback...
}
or use parameter destructuring:
export const CommandInput = ({callback}) => {
// ^--------^---------------- note { }
// ... use callback...
}
Currently, you're trying to use the props object itself as the onChange, which is why you get the error.
Example using destructuring:
const Example = ({callback}) =>
<div onClick={callback}>Click me</div>;
ReactDOM.render(
<Example callback={() => console.log("Clicked")} />,
document.getElementById("root")
);
<div id="root"></div>
<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>

Categories

Resources