Handle onSubmit in react child components - reactjs

What is the most elegant and react-like way of handling an onSubmit event on child componenets.
Currently I have a parent form component containing all the corresponding fields. When I click submit the parent component handles everything and sens a POST request to the server with the data.
The problem now is, the form needs to do more stuff, showing fields for a referenced resource, and creating those references on the server, similar to the parent component.
Separating this to multiple submit buttons is no problem, but I want ONE submit button, and I want the onSubmit handling logic separated in child components. So when I click on submit, the parent creates the main resource on the server, and the child components create their stuff.
How can this be accomplished in react in an elegant way, as react is more suited for passing data from parent to child. How can the children also react on the onSubmit event together with the parent component?

Pretty much as easy as this
import React, { Component } from "react";
import Contact from "./contact";
import classes from "./Component.css";
class App extends Component {
componentDidMount() {}
onSubmit = (e) => {
e.preventDefault();
alert("submitted");
};
render() {
return (
<React.Fragment>
<Contact submit={this.onSubmit} />
</React.Fragment>
);
}
}
export default App;
import React from "react";
const Contact = (props) => (
<React.Fragment>
<div className={classes.Container}>
<button onSubmit={props.submit}>Submit</button>
</div>
</React.Fragment>
);
export default Contact;
If this doesn't work you could save the function to context or redux dependant on your use case.
Feel free to send me your code and i will see what i can do :)

Related

Can not use component using hooks inside redux modal

I have a react redux app and the modal is managed through the redux store. So when user gets to the home page the <Home /> and <Modal /> mount at the root of the application. From there, whenever the user clicks a button that should open the modal, displayModal action is dispatched like so:
const handleModalButtonClick = () => {
this.props.actions.displayModal({
className: 'modal-light',
component: () => <FunctionalComponentThatUsesHooks />
});
};
The <FunctionalComponentThatUsesHooks /> is what is supposed to open up in the modal. But this doesn't seem to work when the component passed is a component using hooks as I am seeing the invalid hooks call error:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:...
The component using hooks is a function component, so I know that's not the issue. I can't seem to figure out exactly why this isn't working. Maybe has to do with the fact that the modal is managed through the redux store or the way component is being passed? Any ideas? Thanks
update: the displayModal action has type SHOW which just just adds the data to the redux store as so:
case SHOW:
return {
...state,
modal: action.payload
};
in the <Modal />, using function mapStateToProps to get the component from the redux store. then the <Modal /> render calls this.modalBody which will render that component from the redux store as so:
get modalBody() {
const {component: InnerComponent} = this.props;
if (!InnerComponent) {
return null;
}
return (
<div className="modal-body" ref={this.modalBodyNode}>
<InnerComponent {...this.props} />
</div>
);
}
the <FunctionalComponentThatUsesHooks /> isn't any specific component, I've tried with multiple different components that use hooks, even simple ones that only use the useState hook like so:
import React, {useState} from 'react';
const FunctionalComponentThatUsesHooks = (props) => {
const [size] = useState(props.size);
return (
<button className={`btn-${size}`}>{props.children}</button>
);
};
export default FunctionalComponentThatUsesHooks;

How can I handle an onsubmit event in a parent component from the button?

In my Parent component, there is a form and for submit button it is mentioned in child component.
For example, on hyperlink in the parent component (which is having lots of input data), I'm displaying a popup which is a child component and in this popup there is a submit button, which is also in the child component.
How can I handle OnSubmit method of the parent component from the child component's button click?
Did you store and maintain all input data in parent? If No, you need to store input data in parent component (or using Context Api and store data there)
Create handleSubmit in parent component and pass it as a callback function from parent to button click component, when button is clicked, call the callback function to trigger a handleSubmit function in parent.
Simple demo
import React, { useState } from "react";
export default function App() {
// store all form data in this parent
const [formData, setFormData]= useState({})
const handleSubmit=() =>{
// handle and summit your formData here
}
return (
<div className="App">
{/* Crate a Form compoent and render it here */}
<Button onSubmit={handleSubmit}/>
</div>
);
}
export const Button = ({onSubmit}) =>{
return(
<button onClick={onSubmit}>Submit</button>
)
}
Here is another solution to call parent component function using React props(Property).
[React Property:][1]https://reactjs.org/docs/components-and-props.html
// Parent Component
import React from "react";
// import child component
class parent extends React.Component{
parentClassFunction = () => {
console.log("parent called")
};
render(){
<Child
parentClassFunction={this.parentClassFunction}
/>
}
}
export default parent
// Child Component
class Child extends React.Component{
onClickSubmitButton = () =>{
this.props.parentClassFunction()
};
render(){
<button onClick={this.onClickSubmitButton}>CLICK</button>
}
}
export default Child

Sending a React.FunctionComponent<React.SVGProps<SVGSVGElement>> as a prop to another component

I'm attempting to import a React functionComponent from an SVG and then send that to another component as a prop to render that svg. With the setup below, this compiles fine, but eventually crashes when trying to render the svg in browser with:
Error: Objects are not valid as a React child (found: object with keys {$$typeof, render}). If you meant to render a collection of children, use an array instead.
Classes below are simplified. But the gist of what I'm trying to do is:
In overlay.tsx:
import { ReactComponent as icon } from "/icon.svg";
import CustomItem from "/customItem";
const Overlay: React.FC<OverlayProps> = () => {
return (
<div>
<CustomItem icon={icon}/>
</div>
);
export default Overlay;
}
and in customItem.tsx:
import React from "react";
export interface CustomItemProps {
icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = ({icon}) => {
return (
<div>
{icon}
</div>
);
};
export default ApplicationsDropdownItem;
I assume my problem is somewhere around the syntax of {icon}, but I can not for the life of me find out what I'm suppose to use instead.
Answer
The icon you are importing is a component, therefore it must be called to render the JSX.
<Icon {...props}/> (correct) or {Icon(props)} (not recomended)
Since it is a component, you should also name it Icon and not icon.
Take a look at this blog post that explains SVGR.
TL;DR - Best approach for rendering components
A. Call the component in your render method with component syntax <MyComponent/> not MyComponent().
B. Instantiate your component as a variable, and pass that to your render method's JSX block.
More info
#DustInCompetent brought to light the issue of calling a component as a function inside a JSX block.
As explained here and here, that will lead to react not registering a components hooks and lead to state and other problems.
If you are implementing a High Level Component (HOC), then you should not call a component within the render method (return statement in functional components), as this leads to problems for similar registration issues of the component.
import React from "react";
import { ReactComponent as SampleIcon } from "/sample_icon.svg";
export interface CustomItemProps {
Icon: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
}
const CustomItem: React.FC<CustomItemProps> = (props) => {
const Temp = props.Icon as React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
return (
<div>
<Temp/>
</div>
);
};
<CustomItem Icon={SampleIcon}/>
I think you should use <Icon /> instead of {icon} because it's a component.

How to store user input data in console log on form submit from child to parent component?

I have my child components within my parent component and I would like to be able to console log what data a user has submitted. However, being new to react I am not sure how I should do this?. Would love some help!
import React, { Component } from 'react';
import './App.css';
import PageOne from './Components/PageOne';
import PageTwo from './Components/PageTwo';
import PageThree from './Components/PageThree';
import PageFour from './Components/PageFour';
import PageFive from './Components/PageFive';
import PageSix from './Components/PageSix';
import PageSeven from './Components/PageSeven';
import { Input, Dropdown, TextArea, Form, Button, Header } from 'semantic-
ui-react'
class App extends Component {
render() {
return (
<div className="App">
<PageOne />
<PageTwo />
<PageThree />
<PageFour />
<PageFive />
<PageSix />
<Button>
Submit Form
</Button>
<br/>
<br/>
</div>
)
}
}
export default App;
We can pass a callback through the Parent component as props to Child and invoke it after handling the form submission.
You cant really "store" it in the console, What are you trying to do exactly ?
Do you want to save the user submitted data to use later in a part of your App? if so you could pass a call back to set the state in your Parent component with the data submitted, this way you can share the data with other Child components through the Parent state
The important thing to understand here is that we pass callbacks as props to Child components when we need to pass data to Parent components and save it to our state this way our Parent can share the state with the rest of our App.
function Parent() {
const logAfterSubmit = data => {
console.log("user submitted data", data);
};
return (
<div className="App">
<Child logAfterSubmit={logAfterSubmit} />
</div>
);
}
function Child({ logAfterSubmit }) {
const [input, setInput] = useState("");
const handleSubmit = e => {
e.preventDefault();
// do something with the data
// call our function from parent with the data
logAfterSubmit(input);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" onChange={e => setInput(e.target.value)} />
<button type="submit">Submit</button>
</form>
);
}
SandBox
Welcome to stackoverflow. React is an amazing tool for developing websites. Unique to React is a concept called "state" which is essentially what you're looking for. "State" is React's ability to keep track of data and use it accordingly. In your case, you want to record what a user submits in your child component.
Let's consider the following code:
class Parent extends React.Component{
//define state here which is typically an empty object
state = {
text: ""
}
//this is an event handler we have set up to take the data from state
handleOnSubmit = () => {
event.prevent.default() //stops page from refreshing
console.log(this.state.text)
}
//event handler that is triggered every time a user makes a change to the input
handleOnChange = (event) => {
//unique method in React that lets you update the component state
this.setState({
text: event.target.value
})
}
//all class-based components are called with render
render(){
return(
//we can pass down event handlers/functions we've defined into child components.
<Child handleOnChange={this.handleOnChange} handleOnSubmit={this.handleOnSubmit}/>
)
}
}
Now for our Child Component
//this is known as a stateless-functional component. no need to keep track of state.
const Child = (props) => {
return(
<form onSubmit={props.handleOnSubmit}>
//onChange is a unique-event listener that gets called anytime characters changes inside an input tag. So in this case we update our Parent's state every time we type.
<input onChange={props.handleOnChange} name="text" />
</form>
)
}
So a high-level summary of this. We set up a parent-component and defined functions inside of it that will help keep track of "state" or data. Then we pass down those functions as properties to our Child component. Our child component uses those functions and when they are called, they will update the state in our Parent component. Thus giving you a controlled-input flow, where you are constantly keeping track of data coming into your forms.

where should I dispatch my redux

I am learning react and redux, and I just have a small question about where should I dispatch and share my redux store to react components, should I share and dispatch my store in whatever components that need the store or I should share and dispatch my store in one main component and share that values as props to order components?
for example I have these three components and I have my states stored in one FlashCard component and share that states to Cardlist component using props and then the CardList component will send that props to Card component. is it the right thing to do? and also in card component I use dispatch because it seem more convenient, but should I use dispatch in my main component FlashCard as well and pass the change to Card component? thanks for your help.
import React from 'react';
import CardList from './cardList';
import {connect} from 'react-redux';
const FlashCard =(props)=>{
return (
<div>
<CardList
cards={props.cards}
/>
</div>
)}
const mapStateToProps=(state)=>({
cards:state.cards
})
export default connect(mapStateToProps,null)(FlashCard)
and
import React from 'react';
import Card from './card';
const CardList =(props)=>{
const card=props.cards.map((c,i)=>(
<Card
key={i}
card={c}
/>
))
return(
<div>{card}</div>
)}
export default CardList
and a Card component to render all the cards
import React from 'react';
import {connect} from 'react-redux';
import { showCardInfo, hideCardInfo } from '../redux/flashCardRedux';
const Card =(props)=>{
const onCardClick=()=>{
console.log(props.card.id)
}
return (
<div>
{props.card.showInfo?
<div
onClick={()=>props.dispatch(hideCardInfo(props.card.id))}
>{props.card.name}</div>
:
<div
className='card'
onClick={()=>props.dispatch(showCardInfo(props.card.id))}
>
<div className='img'>
<img src={props.card.img}/>
<h3>{props.card.name}</h3>
</div>
</div>}
</div>
)}
export default connect()(Card)
For me, I have found it best practice to only refer to dispatch in the main component and only pass in what the child components require as properties. This not only means that you are not passing dispatch around to everything, but also allows for unit testing of the smaller components if required.
It also keeps the smaller components much "lighter" in that they only have what they need, and can easily then be rendered in other areas of your app.
In the future, if you ever wanted to swap out Redux for something similar, it means you are then only having to edit code in the main component rather than everywhere in your system.
Its always recommended to use dispatch in parent component because
child is also part of parent but as per requirement you can shift.
as you have parent to child connection either you can connect in
parent and pass data as `props` or either you can take out in child
component itself, it depend upon how complex your parent and
child.however for smaller component always use as props else go for
component wise connect.
for more info [follow this](https://reactjs.org/)

Resources