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>
);
}
}
Related
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 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);
}
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.
I'm using React v16 (latest) and I'm trying to create a general form component, that uses props.children.
export class MyForm extends React.Component {
render() {
return (
<div>
<div className="form">
<h3>{this.props.formName}</h3>
<div>
{React.Children.map(this.props.children, t => {return <span>{t}<br></br></span>;})}
</div>
<input type="button" value={this.props.formName} onClick={this.handleClick}/>
</div>
</div>
)
}
}
I want to create small form that just are able to create a meaningful json object and send it in POST
This is an example of such usage:
<MyForm>
<input type="text" name="a1"></input>
<input type="text" name="a2"></input>
</MyForm>
And I want to have many such small forms. Problem is I want each child (props.children) to have an onChange event -
onChange(event) // name is "a1" or "a2", like in the example aboce
{
var obj = {};
obj[name]=event.target.value;
this.setState(obj);
}
-so that I don't need to manually add onChange for each such child
-I guess another solution is to create a component, but I want the form to be flexible for each kind of sub-element (input text, text area, radio buttons,...) and I just want them all to have similar onChange that will set the name of the component and its value to the state...
I tried adding an onChange property in consturctor and in different hooks, but got:
cannot define property 'onChange', object is not extensible
So when are where (if at all) can I add an onChange dynamically to props.children
This is a great use case for a Higher Order Component. You can use a HOC to wrap and add the onChange prop to any component:
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name]=e.target.value;
this.setState(obj);
}
render() {
return <WrappedComponent {...this.props} onChange={this.onChange} />
}
}
}
...
import Input from './Input';
class MyForm extends Component {
render() {
return (
<form>
...
<Input type="text" name="a1" />
...
</form>
)
}
}
export default MyForm;
....
import WithOnChange from './WithOnChange';
const Input = (props) => (
<input {...props} />
);
export default WithOnChange(Input);
EDIT:
Another option is to move your children map into a higher order component and then create a custom <Form /> component:
const Form = () => {
return <form>{this.props.children}</form>
};
export default WithOnChange(Form);
const WithOnChange = WrappedComponent => {
return class extends Component {
onChange = e => {
const obj = {};
obj[name] = e.target.value;
this.setState(obj);
}
render () {
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, { onChange: this.onChange });
});
return <WrappedComponent {...this.props}>{children}</WrappedComponent>
}
}
}
#user967710, can you please test the following code:
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(event) {
debugger;
var obj = {};
obj.name = event.target.value;
this.setState(obj);
}
<MyForm formName="myForm">
<input type="text" name="a1" onChange={this.onChange}></input>
<input type="text" name="a2" onChange={this.onChange}></input>
</MyForm>
I'm using Higher Order Components to decorate my components.
const HOC = (WrappedComponent) => (props) => {
return (
<span>
<p>HOC Comp</p>
<WrappedComponent {...props}/>
</span>
)
}
I do like this pattern explained here: React Higher Order Components in depth
However I have a problem because the HOC causing React to recreate my component tree instead of updating the tree. This is nicely explained here React Reconciliation. HOC returns an anonymous function whereby React doesn't know it is actually rendering the same component. This is bad for performance and makes my input field lose focus.
How could I use HOC components without React recreating my tree on every render()?
Example code:
class Input extends React.Component {
componentWillMount() {
console.log('input component will mount');
}
componentWillUnmount() {
console.log('input component will unmount');
}
render() {
return (
<span>
<input value={this.props.value} onChange={this.props.onChange}/>
</span>
);
}
}
const HOC = (WrappedComponent) => {
const Help = (props) => {
return (
<span>
<WrappedComponent {...props}/>
<p>{props.help}</p>
</span>
)
};
return Help;
}
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.state = {value : 'start value'}
}
onChange(event) {
this.setState({value : event.target.value});
}
render() {
const Element = HOC(Input);
return (
<span>
<Element
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</span>
)
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('container')
);
See Fiddle example (see in your browser's console to see the mount and unmount logs from the input component every time you change the input and lose your focus)
You don't have to create Element in the render function. Instead you can create it in the constructor:
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.state = {value : 'start value'};
this.element = HOC(Input);
}
...
And use it in your render function like this:
<span>
<this.element
value={this.state.value}
onChange={this.onChange.bind(this)}
/>
</span>
If needed you can update this.element in componentWillReceiveProps() or componentWillUpdate().
UPDATE: fiddle
This is the way how I extended a functional component with a controlled input element via children and it does not lose focus.
/* import React, { Fragment, useState } from 'react' */
const { Fragment, useState } = React
/* HOC */
const withInput = Base => (props) => {
const [inputValue, setValue] = useState()
return <Base children={
<Fragment>
{props.children}<br />
<input
value={inputValue}
onChange={({ target: { value }}) => setValue(value)}
placeholder="input from hoc" /><br />
<span>inputValue: {inputValue}</span>
</Fragment>
} />
}
/* Component */
const MyComponent = ({children}) => (
<span>
<span>own elements</span><br />
{children}
</span>
)
const MyComponentWithInput = withInput(MyComponent)
ReactDOM.render(<MyComponentWithInput>passed children</MyComponentWithInput>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>