Inheritance in react component - reactjs

Please consider a following case:
I have created a "baseUser" component which is a form having three fields username, password and name. Now I want this form in three different applications : Application1, Application2 and Application3.
In Application1, User component should use this baseUser component but want only two fields (username, password) from baseUser state and should also have two additional fields which is first-name and last-name.
In Application2 and Application3, User component should work same as the baseUser.
Also all the actions, events, states should be able to work alone and be able to overridden.
The render method should also be overridden as their can be different UIs for different applications.
How can we achieve this functionality using react components? Does inheriting "baseUser" in applications using "extends" cause any issue (Or is it correct way to do it)?

React does not use Inheritance. You can't extend a react component. React basically work on the Composition. Composition means, creating small parts and combine them together to make a complete part.
Now, in your situation. There are only 3 input fields into your baseUsr component and you want to use only two of them into your application1. Simply you can't do this. You have to render complete baseUsr component.
You are getting the concept of components wrong. Think about components like a function or part of UI that is abstract or can be used in standalone.
For example, you can create a Header component because it can be used in isolation and can be used on multiple pages. But an input field in a form can not be used in isolation.
This confusion is created because you create components just like classes in javascript but they are the basic building block of UI.
For more information read Composition VS inheritance on React docs

You can write your baseUser this way:
class BaseUserForm extends Component {
handleChange = ({ currentTarget: input }) => {
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data }); //state is in inheriated form.
};
handleSubmit = e => {
e.preventDefault();
//Form validation
if (!errors) this.doSubmit();
};
renderInput = (name, label, type = "text") => {
return (
<Input
name={name}
label={label}
type={type}
error={this.state.errors[name]}
value={this.state.data[name]}
onChange={this.handleChange}
/>
);
};
renderButton = (label, type = "submit") => {
return (
<button
type={type}
className="btn btn-primary"
// disabled={this.validate()}
>
{label}
</button>
);
};
}
export default BaseUserForm;
Then depending on your Application, you can add/remove input fields as needed.
class AppForm extends BaseUserForm{
state={
data:{username:"",
password:""}
};
doSubmit = () => {
//Do your submit here.
};
render(){
return(
<form onSubmit={this.handleSubmit}>
{this.renderInput("username", "User Name")}
{this.renderInput("password", "Password")}
//Add more field as needed.
</form>
);
}
}

Related

Can I programmatically update the values of React bootstrap <Form.Control> after it's been updated?

Is it possible to update the value of a React Bootstrap <Form.Control> after it's been edited by a user?
I've created a small Bootstrap form in React with four controlled fields. Details are below. Submission works, but I'd like a way to update the values of various fields programmatically, both to reset the form and to fill in sets of common defaults.
My code, in which various buttons call setFormContents, works until a field is updated. After this, updating the value passed into the defaultValue of the <Form.Control>. This makes sense, but I'm not sure what is the preferred way to proceed.
The variables formContents and setFormContents are assorted with the parent element of <MyForm>.
Details
The state of the form and the associated updating function is provided via props
define MyForm(props)
return <Form
onSubmit={(e) => {
e.preventDefault();
onSubmit(props.formContents);
}}
>
<Form.Group className="mb-3" controlId="field1">
<Form.Label>Field 1</Form.Label>
<Form.Control
defaultValue={props.formContents.val1}
onChange={({ target: { value } }) =>
props.setFormContents({ ...props.formContents, val1: value })
}
/>
</Form.Group>
///... Other <Form.Group>s
</Form>
In my case, the update was for all fields. My solution, which is modeled on this StackOverflow response was to reset the form before updating it.
Create a state that will store reference to the DOM <form> element.
const [formDOM, setFormDOM] = useState(null);
Use the ref= property of the <Form> element to capture the DOM element. The state will have type ?HTMLFormElement
<Form
ref={(form) => props.setFormDOM(form)}
...
>
Provide this ReactDOM to elements that update the form. Reset the form before any updates.
onClick={(e) => {
if (props.formDOM != null) {
props.formDOM.reset();
}
props.setFormContents(...);
}}
You can also use useRef() to access your From.Control programmatically :
const inputRef = useRef(null);
[...]
<Form.Control
ref={inputRef}
defaultValue={props.formContents.val1}
onChange={({ target: { value } }) =>
props.setFormContents({ ...props.formContents, val1: value })
}
/>
And then access to the value :
inputRef.current.value = someValue;
In your case, in your onClick function :
onClick={(e) => {
...
inputRef.current.value = "";
}
This approach is less concise because you have to do it on every fields but allows to independently control the content of each From.Control.

Is there a standard design pattern for viewing + editing a record in React / Material-UI?

Frequently I have a record of data to display to the user, and I want to display an Edit button allowing that data to be modified:
Create Pizza:
sauce: Margherita
cheese: Gorgonzola
radius: 12
[EDIT] [ORDER]
Is there a standard pattern for this in React / Material-UI? It seems silly to implement the same view twice, once with (readonly) <div>s or whatever and once with <input>s. Is there a better way? (Perhaps there's a Material-UI component with an "editable" prop, for instance.)
Take a look at api for text field. You'll see that you have a couple of ways of doing what you are looking.
You could set disabled={true}
You also get inputProps, as an example
<TextField id="time" type="time" inputProps={{ readOnly:true }} />
All the other elements should also have one or the other or both.
I usually have a wrapper component that accepts props such as ({isReadOnly: boolean}) & within that is the "native" React MUI component & then I send back editable or a read only component back to the caller.
EDIT:
interface MyTextFieldProps extends StandardTextFieldProps {
isEditable?: boolean;
// send in any other props necessary to render your non editable
// portion
}
const MyTextField: FC<MyTextFieldProps> = (props) => {
const { isEditable, ...other } = props;
return <>{props.isEditable ? <TextField {...other}></TextField> : <span>text:{props.value}</span>}</>;
};

Enzyme change another input field

I have a simple React component which has one email input field and a checkbox like this:
interface MyProps {
onSubmit?: (form: any) => void;
}
class Preferences extends React.Component<MyProps> {
state = {
primaryEmailCheckbox: false,
primaryEmail: "",
};
onPrimaryEmailChange = e => {
this.setState({ primaryEmail: e.target.value });
let checkbox = document.getElementById("primaryEmailCheckId") as HTMLInputElement;
checkbox.disabled = false; //<<< checkbox is null. lets say this is line 18
}
}
render() {
return (
<StaticContent />
<h3>Email Address</h3>
<div className="ui mini icon input">
<input type="email" value={this.state.primaryEmail} placeholder="Enter email..." onChange={this.onPrimaryEmailChange} />
</div>
<div className="ui checkbox">
<input type="checkbox" disabled={true} id="primaryEmailCheckId" onChange={e => this.setState({ primaryEmailCheckbox: e.target.checked })} /><label> Continue to receive further email communications from Us </label>
</div>
);
}
}
export default Preferences;
When anyone enters any thing on the email field, the checkbox becomes visible for user to check it or keep it unchecked.
When I run the application, it works as expected. But when I test it, it says checkbox is null (at line 18), so you cannot set disabled on that.
This is a test to test Preferences Component:
import * as React from "react";
import { shallow } from "enzyme";
import Preferences from "../../components/Preferences";
test("Preferences shows email and checkbox", () => {
const wrapper = shallow(<Preferences onSubmit={() => { }} />);
wrapper.find("input").at(0).simulate("change", {
target: {
value: "a#b.c",
}
});
expect(wrapper.find("input").state().value).toEqual("a#b.c");
});
This throws Null exception at line 18. The thing is, the value a#b.c is passed correctly and I verified it by placing log statements.
But, when I try to change the value of input type email, it calls a onChange method which tries to access (and change) the value of another input field.
I don't know how to change the value of 2nd input type which is a checkbox. How I can I make it work? Any help is appreciated.
This is because the shallow(...) rendering method provides a limited set of interaction patterns and document.getElementById(...) is not one of them. You should be able to get what you want using the following:
const wrapper = mount(<Preferences />, { attachTo: document.body });
(Docs for the above code. You can swap out document.body for the relevant equivalent if you're using something like JSDOM or similar).
That said... using document.getElementById at all is a huge red-flag in React development. Because React lets you interact with a virtual DOM and handles the application of that to the real DOM, fiddling with the real one yourself is a great way to end up with all sorts of bugs. A much better option would be to use refs to access the checkbox in the "React way", or just make checkboxEnabled: boolean part of your state and update it inside your onPrimaryEmailChange() method.

React High Order Components

Lets says I have an array of objects where each object have structure like below
obj = {
name:"Name1",
description:"Description1",
activeState:1,
rating:5
}
const User = (obj) => {
let userActiveState = (obj.activeState === 1) ? 'Active' : 'Non Active';
return (
<tr>
<td>{obj.name}</td>
<td>{obj.description}</td>
<td>{userActiveState}</td>
</tr>
);
}
User.propTypes = {
name: PropTypes.string
description: PropTypes.string
activeState: PropTypes.number
}
User.defaultProps = {
name: "Not Available"
description:""
activeState: 0
}
I use this array to create UI using User(a stateless functional react component), but before the stateless functional react component spits out the UI I want to make some modification to the object properties which are required by the
UI (example using text instead of number for activeState) and not all the object properties are required too.
Where would I remove unwanted properties so that I can have defaultProps and proptypes defined only for the required properties and would I use a high order component which transforms and filters the obj properties?
You don't need a HoC here - a simple composition is enough:
const Component = ...;
const ComponentButDifferentFormats = ({propToSkip, propToRename, ...props}) => (
<Component
{...props}
propRenamed={propToRename}
propX={parseFloat(props.propX)}
propY={'' + props.propY}
/>
);
With this approach, you'll decouple the transformation logic from the real UI. It's really useful for example with an API response. Creating a HoC is also an option: it might be parametrized with formats, filters or even the component itself. One more function in the above example: Component => ....

How to clear some fields in form - Redux-Form

I'm working on a page which has many input validations and logical bindings on it and every sprint iteration the page size increasing. So that, I have to find a beautiful and scalable solution.
Imagine, when user select a value from dropdown as 'A', some fields must be disabled, some fields must be cleared and some fields initilized with default values. I can change one related field (doesn't have validation rule like regexp or lenght constrait) value with some little code like
this.props.dispatch(change('xForm','xField','xValue' ))
My problem is that when I need to clear multiple fields,
It always blocked by my validation method and clear operation is failed ( Note : I supposed to be like that but not like that)
.
I tried some strategies as below but y,z,w fields have some text and it triggered validation rule and hanled errors. So that, inputs have still old values, not cleared ones.
//Clear
this.props.dispatch(change('xForm','yField','' ))
this.props.dispatch(change('xForm','zField','' ))
this.props.dispatch(change('xForm','wField','' ))
What are the best practises for clear inputs or assign some values to inputs in redux-form for pages which have highly dependent inputs.
I have been researching for 2 days but I couldn't find any optimal solution. (redux normalizer, redux form utils etc.)
Thanks.
This worked for me:
resetAdvancedFilters(){
const fields = ['my','fields','do','reset','properly']
for (var i = 0; i < fields.length; i++) {
this.props.dispatch(change('formName',fields[i],null))
this.props.dispatch(untouch('formName',fields[i]))
}
}
Using the below, it clears the respective multiple fields and also clears the errors if any for those respective fields.
Common function to reset multiple fields.
import {change, untouch} from 'redux-form';
//reset the respective fields's value with the error if any
resetFields = (formName, fieldsObj) => {
Object.keys(fieldsObj).forEach(fieldKey => {
//reset the field's value
this.props.dispatch(change(formName, fieldKey, fieldsObj[fieldKey]));
//reset the field's error
this.props.dispatch(untouch(formName, fieldKey));
});
}
Use the above common function as,
this.resetFields('userDetails', {
firstName: '',
lastName: '',
dateOfBirth: ''
});
Wow, that's some complicated logic. The absolute ideal way would be to use the plugin() API to change values in the reducer when your other field values change. It's a complicated API, because you have total control to mess things up in the reducer, but you have a complicated requirement.
However, dispatching three change() actions like you said you are doing should also work fine. You might want/need to untouch() the fields as well if they have Required validation messages that you don't want to show yet.
It always blocked by my validation method and clear operation is failed.
Can you elaborate on what that means, show a stack trace or console error output?
there.
Let's look at http://redux-form.com/6.0.0-alpha.4/docs/faq/HowToClear.md/
For whole form we can use 4 methods:
A) You can use the plugin() API to teach the redux-form reducer to respond to the action dispatched when your submission succeeds.
B) Simply unmount your form component
C) You can call this.props.resetForm() from inside your form after your submission succeeds.
D) You can dispatch reset() from any connected component
Try to use the dispatch function in meta object with following payload:
meta.dispatch({
type: '##redux-form/CHANGE',
payload: null,
meta: { ...meta, field: name },
})
That should do the trick for any field you want.
I found a better way for this,
We can use initialize action creator of RF.
let resetFields = {
firstName: '',
lastName: '',
dateOfBirth: ''
}
this.props.initialize(resetFields, true); //keepDirty = true;
If the keepDirty parameter is true, the values of currently dirty fields will be retained to avoid overwriting user edits.
clear multiple fields
import {change} from 'redux-form';
const fields = {name: '', address: ''};
const formName = 'myform';
const resetForm = (fields, form) => {
Object.keys(fields).forEach(field => dispatch(change(form, field, fields[field])));
}
resetForm(fields,formName);
If we are talking about entering fields into a form, and then navigating away before submitting, this is the best solution. Place this in your componentDidMount() with the fields you want cleared.
this.props.initialize({
firstName: null,
lastName: null,
bankRoutingNum: null,
newBankType: null
});
For a functional component, you can you change function of RF.
Pass change as props to your component and use it like change(Field_name, value)
For example:
import { reduxForm } from "redux-form";
const Screen = ({
handleSubmit,
change
}) => {
onChange = () => {
change(Field_name, value)
}
return (
// Your code
)}
You can use the clearFields prop or action creator to clear multiple fields.
For example, in this simple form, I use the clearFields prop to clear the firstName and lastName fields.
import React from "react";
import { Field, reduxForm } from "redux-form";
const SimpleForm = (props) => {
const { clearFields } = props;
const clearName = (e) => {
e.preventDefault();
clearFields(false, false, ...["firstName", "lastName"]);
};
return (
<form>
<div>
<label>First Name</label>
<Field name="firstName" component="input" type="text" />
</div>
<div>
<label>Last Name</label>
<Field name="lastName" component="input" type="text" />
</div>
<div>
<label>Occupation</label>
<Field name="occupation" component="input" type="text" />
</div>
<button onClick={clearName}>Clear Name</button>
</form>
);
};
export default reduxForm({
form: "simple"
})(SimpleForm);

Resources