The best way to pass function as props in Reactjs - javascript

I have two components A and B. Component A has a restriction on button function based on conditions, I want Component B's button to make use of the restriction function in Component A
//Component A
state = {
disabled = true,
password='Admin'
}
authorize = (e) => {
var passcode = 1234
if (passcode == this.state.password) {
this.setState({ disabled: false })
} else {
alert("Access code is incorrect please enter the correct access code")
}
}
render() {
return (
<div>
<form>
<input
style={{ width: 200 }}
placeholder="Enter Admin Access code "
onChange={(e) => this.setState({ password: e.target.value })}
/>
<Button bsStyle='primary' onClick={(e) => this.authorize(e)}> Enter</Button>
</form>
// Button disabled waiting for authorize function to be called
<Button disabled={this.state.disabled} >I am disabled</Button>
</div>
);
}
Component B
render() {
return (
// want to be able to do something like
<Button disabled={this.props.disabled}>I am disabled too</Button>
);
}
I want to make use of the authorize function in Component B, don't really know how to

Related

how to handle these multiple forms?

the requirement is a bit tricky. dunno how to explain it but I'll try.
so I've 20 forms on my web page. and there's only one submit button that'll bulk-submit all the forms. but filling out all forms isn't required. the user will fill up as many forms as they like. but whichever form they fill up, must be fully filled. means all inputs are required on each form.
so I need to write a logic that'll make all the inputs required in a form if any of the input is filled there. I know I can use the onChange function and write a logic to make all the input required in one form. but I also need to remove the required from the inputs, if all the input field is again cleared. and I think removing the required from the inputs is the main complexity here. Because while adding required i can simply check if any of the inputs have value in it (using onChange on every input). but if I do the same for removing required, I can't be assured if one input field is cleared or all the inputs are cleared from that form. so in simpler words, I need to sync all the inputs on each form.
[NOTE-1: I'm in a React environment, so I've all the facilities of states and stuff]
[NOTE-2: I've made a react component for the form and looped over it 20 times. so you can think that as one form]
[NOTE-3: This is one of my client's projects. so I can't have changes to the requirements]
This is a pretty "business specific" problem, but I would tackle it along these lines. You may need to make adjustments to fit your exact requirements, but the general gist is there.
The key is to treat the "required" flag for each input as "derived" or calculated state. You said "but I also need to remove the required from the inputs" - I don't think that's entirely true, or doesn't fit the react model. You just need to check if other fields are populated in the current form in the current render.
const { useState } = React;
const { render } = ReactDOM;
const forms = [
{
inputs: ["field1", "field2"]
},
{
inputs: ["field3", "field4"]
}
];
function MegaForm() {
const [values, setValues] = useState(() => {
const values = {};
forms.forEach((form) => {
form.inputs.forEach((input) => {
values[input] = "";
});
});
return values;
});
const submit = () => {
console.log(values);
};
const isRequired = (formIndex) => {
return forms[formIndex].inputs.find(
(inputName) => values[inputName] !== ""
);
};
return (
<div>
{forms.map((form, i) => (
<form key={i}>
<h2>Form {i}</h2>
{form.inputs.map((input, j) => (
<div key={j}>
<label>
{input}
<input
value={values[input]}
onChange={(e) =>
setValues({ ...values, [input]: e.target.value })
}
required={isRequired(i)}
/>
{isRequired(i) ? "*" : ""}
</label>
</div>
))}
</form>
))}
<br />
<br />
<button type="button" onClick={submit}>
Submit
</button>
</div>
);
}
render(<MegaForm />, document.getElementById("app"));
CodePen: https://codepen.io/chrisk7777/pen/RwYWWqV?editors=0010
If you have all the forms with the same fields you could go with a solution like this:
export function FormsContainer() {
const [formData, setFormData] = React.useState({});
function onChangeGenerator(i: number) {
return (e) => {
setFormData((data) => ({
...data,
[i]: {
...data[i],
[e.target.name]: e.target.value,
},
}));
};
}
function fieldHasValue(value) {
return value !== null && value !== undefined && value !== '';
}
function formHasValidFields(i) {
return (
formData[i] &&
Object.keys(formData[i]).some((key) => fieldHasValue(formData[i][key]))
);
}
function submit() {
const result = Object.keys(formData).reduce((acc, i) => {
if (formHasValidFields(i)) {
acc.push(formData[i]);
}
return acc;
}, []);
console.log(result);
}
return (
<form
onSubmit={(e) => {
e.preventDefault();
submit();
}}
>
{[0, 1, 2, 3, 4, 5].map((i) => (
<SingleForm
key={i}
onChange={onChangeGenerator(i)}
required={formHasValidFields(i)}
/>
))}
<br />
<br />
<button type="submit">Submit</button>
</form>
);
}
function SingleForm({
required,
onChange,
}: {
required: boolean;
onChange: (e) => void;
}) {
return (
<React.Fragment>
<hr />
<input name="prop1" onChange={onChange} required={required} />
<input name="prop2" onChange={onChange} required={required} />
</React.Fragment>
);
}
StackBlitz: https://stackblitz.com/edit/react-ts-utrgbj

Clear sub-form values on conditional hide of nested fields

Using React+Formik, I want to create a reusable component that we can use to conditionally show/hide nested subforms (of any complexity).
Every time it becomes hidden, we wish to clear the values so that those values don't get submitted.
Below, a simple hide/show component called OptionalFeature is shown.
const OptionalFeature = ({
toggle,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
return null;
}
}
It can be tested by pasting into https://codesandbox.io/s/zkrk5yldz
But as you can see in the demo, making the children invisible does not clear their values. Ideally each child can define it's own clearValue behavior (the example is very simple, we want to have more complex nested forms).
What's the clean solution to clear the fullname field by extending OptionalFeature class in a generic, reusable way?
I already tried creating a cleanup function and calling it from OptionalFeature inside the if-block, but it does not seem very idiomatic.
// Helper styles for demo
import "./helper.css";
import { DisplayFormikState } from "./helper";
import React from "react";
import { render } from "react-dom";
import { Formik } from "formik";
// Generic reusable component to show/hide sub-forms
const OptionalFeature = ({
toggle,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
return null;
}
}
const App = () => (
<div className="app">
<Formik
initialValues={{ email: "", anonymous: false, fullname:"" }}
onSubmit={async values => {
await new Promise(resolve => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleSubmit
} = props;
return (
<form onSubmit={handleSubmit}>
<input
id="email"
placeholder="Enter your email"
type="text"
value={values.email}
onChange={handleChange}
/>
{/* This checkbox should show/hide next field */}
<div style={{ display: "white-space:nowrap" }}>
<label htmlFor="anonymous" style={{ display: "inline-block"}}>Anonymous</label>
<input
id="anonymous"
type="checkbox"
name="anonymous"
value={values.anonymous}
onChange={handleChange}
style={{ display: "inline-block", width: "20%"}}
/>
</div>
<OptionalFeature
toggle={!values.anonymous}
>
{/* Imagine this subform comes from a different file */}
<input
id="fullname"
placeholder="Enter your full name"
type="text"
value={values.fullname}
onChange={handleChange}
/>
</OptionalFeature>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
}}
</Formik>
</div>
);
render(<App />, document.getElementById("root"));
Here is my existing approach, waiting for a better answer:
const OptionalFeature = ({
toggle,
onHide,
children
}) => {
if (toggle) {
return <div>{children}</div>
} else {
// useEffect needed because onHide function could trigger anything.
// Also to avoid calling multiple times.
useEffect(() => {
onHide();
}, [toggle, onHide])
return null;
}
}
Then later invoke a cleanup function onHide:
<Formik
initialValues={{ email: "", anonymous: false, fullname:"" }}
...>
{props => {
const {
values,
isSubmitting,
handleChange,
handleSubmit
} = props;
// to clean up all prop values inside the OptionalFeature
const clearFullName = () =>
{
values.fullname = ""
}
return (
//...
<OptionalFeature
toggle={!values.anonymous}
onHide={clearFullName} // using cleanup Function
>
<input
id="fullname"
placeholder="Enter your full name"
type="text"
value={values.fullname}
onChange={handleChange}
/>
</OptionalFeature>
);
}}
</Formik>
What I don't like here is that as the for becomes more complex with more OptionalFeatures or more elements nested inside the optional feature, it becomes quite hard to check whether all fields inside the nested optional form are being cleaned up or not. Also the properties of useEffect seem hard to test.
I would prefer some kind of nested subform such that I could write something like onHide={handleReset}, and this would be scoped only to fields inside the nested subform, without me having to define a custom handleReset function for that.

Perform HTML Input Validation on React Button Click

This is a simplified scenario.
I have a form with a required input field and a button. The button has an onClick React handler and is of type "button". I want the browser to check the HTML fields and do some initial validation (like it would do if no React were involved and the button were of type "submit"). I imagine I should do something in the handler function, but I am not sure what.
A few things I tried:
Changing the button to type "submit" does perform the check, but also calls the handler, which does not know whether the check succeeded or failed
Adding the handler on the form instead works, but makes the real example harder to maintain because I have a lot of buttons
Thank you
<div id="app"></div>
class MyClass extends React.PureComponent {
render() {
return (
<form action="#">
<input type="text" required/>
<button type="button" onClick={e => this.handle(e)}>Press</button>
</form>
)
}
handle(event) {
// What should I do here?
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))
https://jsfiddle.net/89wr3ot4/
It looks like form has a checkValidity() and reportValidity() API. The answer then becomes
class MyClass extends React.PureComponent {
render() {
return (
<form action="#" ref={this.formRef}>
<input type="text" required/>
<button type="button" onClick={e => this.handle(e)}>Press</button>
</form>
)
}
handle(event) {
const form = this.formRef.current;
if (!form.checkValidity()) {
form.reportValidity()
return
}
// Everything else
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))
You need to create state for input value
const [inputValue, setInputValue] = useState(''); //for functional component
const inputHandler = (event) => setInputValue(event.target.value);
then
<input type='text' value={inputValue} onChange={inputHandler} />
and check in your 'handler' function what you want.
handle(event) {
if (inputValue.length > 0) //do what you want...
}
Following is working example which is modified from above jsfiddle
class MyClass extends React.Component {
state = { value: '', message: ''}
render() {
return (
<form action="#">
<input type="text" required value={this.state.value} onChange={e => this.setState({value: e.target.value})} />
<button type="button" onClick={e => this.handle(e)}>Press</button>
<p> {this.state.message }</p>
</form>
)
}
handle(event) {
// What should I do here?
const { value } = this.state;
if (value === '') {
this.setState({message: 'Invalid!, Please enter valid input'})
} else {
this.setState({message: 'Yeah!, Got Valid input'})
}
}
}
ReactDOM.render(<MyClass />, document.querySelector("#app"))

when I click add button multiple times it should show I am here div with delete button with multiple times

I am trying to build a simple ui so that I can learn react.
right now when I click an add button it will show I am here div and delete button
when I click add button multiple times it should show I am here div with delete button with multiple times.
so I research and found this example https://www.skptricks.com/2018/06/append-or-prepend-html-using-reactjs.html
using this example I implemented the appendData method but still its not adding the div multiple times.
in my console I am able to see how many times divs are added console.log("this.displayData---->", this.displayData);
can you tell me how to fix it.
so that in future I will fix it myself
https://stackblitz.com/edit/react-b2d3rb?file=demo.js
onClick = () => {
this.setState({ showResults: true });
this.setState({ showOrHideSearch: true });
this.displayData.push(
<div style={{ display: this.state.showOrHideSearch ? "" : "none" }}>
{" "}
I am here
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={this.onDelete}
/>
</div>
);
console.log("this.displayData---->", this.displayData);
this.setState({ showdata: this.displayData });
};
First thing is you should not use this.setState multiple times, instead you should do them in one line. And instead of pushing data into class variables, you should set that data into your state variable and the same variable you should use in your render function. It will be good if you can share your complete code..
import React, { Component } from 'react';
import Calendar from 'rc-calendar';
import DatePicker from 'rc-calendar/lib/Picker';
import 'rc-calendar/assets/index.css';
import moment from 'moment';
class CalendarPage extends Component {
constructor(props) {
super(props);
console.log("AsyncValidationForm this.props---->", this.props);
this.state = {
displayData: []
};
}
onClick = () => {
let displayData = [...this.state.displayData];
displayData.push( { text: 'I am here' });
this.setState({ displayData: displayData });
};
onDelete = index => {
let displayData = [...this.state.displayData];
if(index > -1){
displayData.splice(index, 1);
}
this.setState({ displayData: displayData });
};
handleChange = name => event => {
const value = event.target.value;
this.setState(
{
[name]: value,
pristine: false
},
() => {
this.props.handleChange(name, value); //setState username, password of VerticalLinearStepper.js
}
);
};
onSubmit(event) {
event.preventDefault();
var newItemValue = this.refs.itemName.value;
if(newItemValue) {
this.props.addItem({newItemValue});
this.refs.form.reset();
}
}
render() {
const { handleSubmit, pristine, reset, submitting } = this.props;
let { displayData} = this.state;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="submit" value="add" onClick={this.onClick} />
{displayData.length > 0 ? displayData.map(function(data, index) {
return (
<div key={index}>
{data.text} - For testing added index on screen {index}
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={() => this.onDelete(index)}
/>
</div>
)}, this) : null}
</div>
</form>
);
}
}
export default CalendarPage;

How can i clear my input when i'm submit my event (reactJs app)

I have 3 inputs whose value I save and click on my btn, I would like to clear these inputs.......
my function that saves the value of one of my inputs:
onChangeIdentity = (event) => {
this.newPlayer = Object.assign({}, this.newPlayer, { strPlayer:
event.target.value})
}
my input:
<Input style={{width:'30%'}} onChange={ this.onChangeIdentity }
ref='myFormRef' value={ this.newPlayer.strPlayer } type='text'
placeholder='Nom & Prenom'/>
and the function that must clear my input:
addPlayer = () => {
console.log('my new Player: ' , this.newPlayer);
this.setState({
teamPlayers: [...this.state.teamPlayers, this.newPlayer]
})
this.refs.myFormRef.value = ""
}
I tried several ways to declare my refs but nothing works.....
any ideas?
You input's values are driven by the state of the component value={this.newPlayer.strPlayer}. If you want to clear the input's value you need to clear the state which maps to it, for example:
this.setState({newPlayer: {strPlayer: ''}});
After setting the state, the component updates automatically and renders the input as empty.
Here is a full example component:
class MyComponent extends Component {
state = {
inputValue: ""
};
render() {
return (
<div>
<input
type="text"
value={this.state.inputValue}
onChange={event => this.setState({ inputValue: event.target.value })}
/>
<button
onClick={() => {
/* submit this.state.inputValue */
this.setState({ inputValue: "" }); // reset input value
}}
>
submit
</button>
</div>
);
}
}

Categories

Resources