I want to create a component with a simple form, lets say with 4 input fields and a button to submit. When the user clicks submit, a POST request will be made.
But then I want to reuse the same component to make a PUT request. How can I achieve that?
Pass a function from a parent component to you child component (the form):
https://codesandbox.io/s/4x3zjwxwkw
const App = () => (
<div>
<CreateRecipe />
<UpdateRecipe />
</div>
);
class CreateRecipe extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
console.log("Do POST recipe");
}
render() {
return (
<div>
<MyForm title="POST Recipe" onSubmit={this.onSubmit} />
</div>
);
}
}
class UpdateRecipe extends React.Component {
constructor(props) {
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit(e) {
e.preventDefault();
console.log("Do GET recipe");
}
render() {
return (
<div>
<MyForm title="GET Recipe" onSubmit={this.onSubmit} />
</div>
);
}
}
const MyForm = ({ title, onSubmit }) => {
return (
<div>
<h2>{title}</h2>
<form onSubmit={onSubmit}>
<input type="text" />
<button type="submit">Submit</button>
</form>
</div>
);
};
EDIT: Per the comment, you can absolutely separate the POST and GET submit functions, but this duplicates logic a bit. What could be better is to have the <App /> component own the two submit functions, and pass them respectively to <UpdateRecipe /> and <PostRecipe />, maybe even the titles too! I'll let you compose this however you'd like, but hopefully this shows you the flexibility of React.
You can pass in a method as a prop to the component - that way you can define the functionality outside of the component and let it execute it within the components scope.
An example of this would be passing in a onPress event to a Button, or an onClick event to any other component.
You can pass the method into the component as a prop and then reference props in your submit function like this:
submit = () => {
var data = {
method: this.props.method,
...
};
fetch('/url/', data);
}
Related
Would it be possible to access a sub-components properties in the main component in React?
For example I'm trying this: I've got a main component MyComponent and a SubComp sub-component that renders a button. Would it be possible to set the state of MyComponent equal to the tex property of the clicked SubComp?
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({name: SubComp.tex});
}
render() {
return (
<div>
<SubComp onClick={this.handleClick} tex="but1"/>
<SubComp onClick={this.handleClick} tex="but2"/>
<h1>{this.state.name}</h1>
</div>
);
}
};
class SubComp extends React.Component {
constructor(props){
super(props);
};
render(){
return(
<button onClick={this.props.onClick}>Click Me</button>
);
}
}
I've tried to do it in the handleClick method with SubComp.tex but it's obviously not working.
Thanks!
Pass the tex prop in SubComp via the callback:
render() {
return (
<button onClick={() => this.props.onClick(this.props.tex)}>Click Me</button>
);
}
And use it in the handleClick of MyComponent:
handleClick(tex) {
this.setState({name: tex});
}
Yes, this is definitely possible. Your idea of passing the function from the main component was right, but to achieve what you are trying to do, you could pass a parameter to the function, like this:
handleClick(e) {
this.setState({name: e.target.value});
}
Notice that I have added the e as a parameter to your handleClick function. This is the click event received from clicking the button. We then set the state equal to the value from this event.
You can also convert SubComp to functional component and pass it handleClick which you defined inside MyComponent as props:
const SubComp = ({handleClick}) => {
return <button onClick={handleClick}>Click me</button>
}
Then pass it handleClick method like so: <SubComp handleClick={this.handleClick} />
Hi I have this reactjs snippet I need to convert this to a class component (now it is a functional` component)
But this has to be carried out carefully since I try to get values from Form component to the Main component and prints that value within the Main component.
This is my Main.jsx
function Main(props) {
const [firstName, setFirstName] = React.useState('');
return (
<div>
<Form setFirstName={setFirstName} firstName={firstName} />
<p> {firstName} <p/> // this works fine
</div>
);
}
And this is the Form.jsx I need this to write as a class component instead of a functional component which is it now.
function Form({ firstName, setFirstName }) {
const handleChange = (event) => {
setFirstName(event.target.value)
}
return (
<div>
<TextField onChange={handleChange} value={firstName} />
</div>
);
}
I tried to convert this like this, but then it doesn't print the value of firstName inside
export default class Form extends Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (event) => {
this.props.firstName = event.target.value;
};
render() {
return (
<div>
<TextField onChange={this.handleChange} value={this.state.firstName} />
</div>
);
}
}
<p> {firstName} <p/> // and now this wouldn'tt work after converting this to a class component
Can someone please help me?
First of all, I don't understand why you'd want to convert this to a class at all, but I'll assume that you have a good reason.
Do not re-assign props like this: this.props.firstName = event.target.value;. Data flow in React goes one direction. Top -> down. If you need to update a prop, you pass a separate prop that is a function to update the first's value (see docs on lifting state up).
If you are wanting to move the state to the Form component, just update state like this:
handleChange = (event) => {
this.setState({ firstName: event.target.value });
};
But notice this will not update props, so this is probably not what you're looking for.
To be more similar to your functional component setup, don't use state at all in Form and keep it in Main like it was before. This means we can cut out the constructor as well.
export default class Form extends Component {
handleChange = (event) => {
this.props.setFirstName(event.target.value);
};
render() {
return (
<div>
<TextField onChange={this.handleChange} value={this.props.firstName} />
</div>
);
}
}
I am working on react application in which I need to call the API with press of a button and the API response should be transferred to the second component along with displaying the content of second component. How can I do that ?
One way to do this would be to hold the fetched data inside state and pass it as a property to a child component which is conditionally rendered.
Function Components
import React, {useState} from 'react'
class ParentComponent {
state = {
data: undefined
}
const handleClick = async () => {
//fetchData
this.setState({data: fetchedData})
};
return (
<div>
<button onClick={this.handleClick}>Click me </button>
{this.state.data && <Child data={this.state.data} />}
</div>
);
};
const Child = (props) => {
return (
//render data
<div>{props.data}</div>
);
};
Class Components
import React from "react";
class ParentComponent extends React.Component {
state = {
data: undefined,
};
handleClick = async () => {
//fetchData
this.setState({ data: fetchedData });
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click me </button>
{this.state.data && <Child data={this.state.data} />}
</div>
);
}
}
class Child extends React.Component {
render() {
return (
//render data
<div>{this.props.data}</div>
);
}
}
Your question is not very specific. If you want to make an api call there are a lot of tutorials online.
For example: https://reactjs.org/docs/faq-ajax.html
On having an event in one component populate data in another component you should handle this by keeping the data in the parent component that contains both the button component and the display component.
The below is App.js file
import React,{Component} from 'react'
//import logo from './logo.svg';
import './App.css'
import InputComponent from "./components/InputComponent";
import ResultComponent from "./components/ResultComponent";
class App extends Component {
render()
{
return (
<div className="App">
<InputComponent />
<ResultComponent/>
</div>
);
}
}
export default App;
The below is InputComponent
import React,{Component} from "react";
import axios from 'axios';
class InputComponent extends Component{
constructor(props) {
super(props)
this.state = {
owner : "",
repo : "",
}
}
//here event.target.value is setting value of target to this owner
ownerName = (event) => {
this.setState({
owner:event.target.value
})
}
//here event.target.value is setting value of target to this repo
repoName = (event) => {
this.setState({
repo:event.target.value
})
}
render(){
//let submit = this.props;
let {items} = this.state;
return(
<div>
<p>The current Owner is {this.state.owner} and the current Repo is {this.state.repo}</p>
<input type='text' onChange={this.ownerName} value={this.state.owner} placeholder='Enter Username' className='inputFields'/>
<br/>
<input type='text' onChange={this.repoName} value={this.state.repo} placeholder='enter Repository' className='inputFields'/>
<br/>
</div>
);
}
}
export default InputComponent;
The below is Result Component
import React,{Component} from "react"
import axios from "axios";
class ResultComponent extends Component{
constructor() {
super()
this.state = {
items: []
}
this.apiFetch=this.apiFetch.bind(this)
}
apiFetch = () => {
axios.get(`https://api.github.com/repos/${this.props.owner}/${this.props.repo}/issues`)
.then(response => {
console.log(response)
this.setState({
items:response.data,
})
})
.catch(error => {
console.log(error)
})
}
render(){
let {items} = this.state;
return(
<div className='submit'>
<button onClick={this.apiFetch}>Fetch Results</button>
<ul>
{items.map(item=>(
<li key={item.id}>
Issue-title: {item.title}
</li>
)
)}
</ul>
</div>
);
}
}
export default ResultComponent
I want to access the value of owner,repo from InputComponent in ResultComponent in my URL part
'''axios.get(https://api.github.com/repos/${this.props.owner}/${this.props.repo}/issues)'''
but not able to do so, can anyone help me what i am doing wrong. I am not able to figure out the issue I am new to React.
In general, there are the options for passing data between react components :
From Parent to Child using Props
From Child to Parent using Callbacks
Between Siblings :
(i) Combine above two methods
(ii) Using Redux
(iii) Using React’s Context API
Use design pattern like HOC or render Props for sharing code between React components (render code abstrait => good practice for reusable)
In your case, it's good pratice with the design pattern render Props. For example, I propose an example of codes :
class InputComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
owner : "",
repo : "",
}
}
//here event.target.value is setting value of target to this owner
ownerName = (event) => {
this.setState({
owner:event.target.value
})
}
//here event.target.value is setting value of target to this repo
repoName = (event) => {
this.setState({
repo:event.target.value
})
}
render() {
return (
<input type='text' onChange={this.ownerName} value={this.state.owner} placeholder='Enter Username' className='inputFields'/>
{/*
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)}
</div>
);
}
}
class WithInputComponent extends React.Component {
render() {
return (
<div>
<InputComponent render={dataInput => (
<ResultComponent dataInput={dataInput} />
)}/>
</div>
);
}
}
Here the links in more details :
https://en.reactjs.org/docs/render-props.html
https://towardsdatascience.com/passing-data-between-react-components-parent-children-siblings-a64f89e24ecf
There are three answers to this question:
You should set your state as high on the DOM tree as you can so that
you can pass the values down to siblings. In simple terms, if state
is set by the parent of the two, you can just ask for state from the
parent and you're done.
You can use a state management system like Redux, which effectively
does the same thing behind the scenes.
You can use refs, but you probably shouldn't so ignore that.
If I were you, I would just bring my state up to App.js, modify it from InputComponent, and pass that modified state down to ResultComponent.
class App extends Component {
constructor(props){
super(props)
this.state = {//initial values}
}
changeSomething() {
// function that changes your state's values
}
render()
{
return (
<div className="App">
<InputComponent aFunctionProp={changeSomething} />
<ResultComponent inputVals={this.state}/>
</div>
);
}
}
export default App;
Check this out as well:
https://reactjs.org/tutorial/tutorial.html
Remember that when you pass down props through your component, you refer to them by their prop name, not by the value you pass in. So in InputComponent, you'll be looking for aFunctionProp() rather than changeSomething(). That was pretty confusing to me when I first learned React.
I tried to create a custom input component with inputRef (material ui Input component). Looks like the component reference is working but I'm unable to enter any value in the text field after i set the value attribute. I think it's because of the way i implemented the onchange event. I'm not sure what am i missing. Please help.
Here is the codesandbox url
https://codesandbox.io/s/pjlwqvwrvm
Actually you don't need a onChange prop to for get the changed value..
Just get the value from onchange and set the value in state value.
Another mistake is you are not created the constructor, and gave this.props.value to the value prop. That's it not get updated..
Now I created the constructor and give the this.state.value to the value props.
Now you get your onchanged value in custominput component and your submit function also..
import React from "react";
import { render } from "react-dom";
import { Input } from "material-ui-next";
import trimStart from "lodash/trimStart";
import PropTypes from "prop-types";
const defaultProps = {
state: "",
onChange: () => {} // no need
};
const propTypes = {
state: PropTypes.string,
onChange: PropTypes.func
};
class App extends React.Component {
constructor() {
super();
this.state = {
value:''
}
}
handleSubmit(event) {
event.preventDefault();
console.log("state: " + this.state.value); //shows onChanged value in console
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
<CustomInput
labelText="State"
id="state"
value={this.state.value}
onChange={e=> {
this.setState({value:e.target.value})
}}
/>
</form>
</div>
);
}
}
App.propTypes = propTypes;
App.defaultProps = defaultProps;
class CustomInput extends React.Component {
render() {
const {
classes,
formControlProps,
value,
onChange,
labelText,
id,
labelProps,
inputRef,
inputProps
} = this.props;
return (
<div {...formControlProps}>
{labelText !== undefined ? (
<div htmlFor={id} {...labelProps}>
{labelText}
</div>
) : null}
<Input
classes={{
root: labelText !== undefined ? "" : classes.marginTop
}}
id={id}
value={value} ///////// Fixed ////////
onChange={onChange}
inputRef={inputRef}
{...inputProps}
/>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Here is code in sandbox check it..
https://codesandbox.io/s/84rjk4m8l8
You can either go on inputRef - then your value and onChange event are extra - it is called uncontrolled Component. You can see more about it here: https://reactjs.org/docs/uncontrolled-components.html
Or you can do it with value & onChange event - and work with controlled components, you can find more about controlled components here: https://reactjs.org/docs/forms.html#controlled-components
How to solve it (uncontrollable) with inputRef:
class App extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
console.log("input value: ", this.input.value); // will now show you correct input value
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
inputRef={input => {
this.input = input;
}}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Instead of "this.state = input", bind input to something else, because this.state is reserved for local state of React Component, and it won't work with it, not like that.
How to solve it (controllable) with state, value & onChange event:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.state || ''
}
}
handleSubmit = (event) =>{
event.preventDefault();
console.log("state: ", this.state.value); // will now show you correct input value
}
handleChange = (event) => {
this.setState({value: event.target.value});
}
render() {
const {value} = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
labelText="State"
id="state"
onChange={this.handleChange}
value={value}
/>
<Button onClick={this.handleSubmit} color='primary'>Submit</Button>
</form>
</div>
);
}
}
Note that I added the constructor and defined the local state for component, and I'm changing value inside of state with this.setState (because state is immutable, and that's the right way to update it).
In both examples, you are able to get input value inside of handleSubmit method, will you work with controllable or uncontrollable components, it's up to you :)
Use value={this.state} or value={this.value}
Here is Your sandbox code Updated
https://codesandbox.io/s/qqk2qoxmlj
In my case, I was mistakenly using state from parent component in child component, since I had declared child component inside parent itself. Moving whole state to child component solved the problem.