score:1
I think that update the placeholder property using the input ref is a good solution, that way you don't need to update the input value (avoid component re renders), and you can clear the placeholder text on click event:
Home.js
class Home extends Component {
constructor(props, context) {
super(props, context);
this.searchInputRef = React.createRef(null);
this.state = { initial_search_term: { search_term: "" } };
}
componentDidMount() {
var self = this;
typeAnimation("Start typing...", 100, function (msg) {
if (document.activeElement !== self.searchInputRef.current) {
self.searchInputRef.current.setAttribute("placeholder", msg);
} else {
return true; // stop typings
}
});
}
render() {
//...
let fieldsSearchForm = [
{
id: "search-field",
type: "text",
label: "Search Term",
name: ["search_term"],
options: [],
fieldRef: this.searchInputRef,
onClick: () => (this.searchInputRef.current.placeholder = "")
}
];
//...
}
}
FieldMaker.js
class FieldMaker extends Component {
//...
render() {
return (
<>
{this.state.fields.map((item, j) => {
if (item.visibility) {
if (item.type !== "checkbox") {
return (
<Field
id={item.id}
//...other props
fieldRef={item.fieldRef}
onClick={item.onClick}
/>
);
} else {
//...
}
} else {
//...
}
})}
</>
);
}
}
renderTextField.js
const renderTextField = ({
id,
input,
rows,
multiline,
label,
type,
meta: { touched, error, warning },
onClick,
fieldRef
}) => (
<FormControl
component="fieldset"
fullWidth={true}
className={multiline === true ? "has-multiline" : null}
>
<TextField
id={id}
inputRef={fieldRef}
onClick={onClick}
// other props
/>
</FormControl>
);
Utility.js
export async function typeAnimation(text, timing, callback) {
let concatStr = "";
for (const char of text) {
concatStr += char;
await sleep(timing);
const shouldStop = callback(concatStr);
if (shouldStop) break; // stop the loop
}
}
styles.css // to keep the placeholder visible
#search-field-label {
transform: translate(0, 1.5px) scale(0.75);
transform-origin: top left;
}
#search-field::-webkit-input-placeholder {
opacity: 1 !important;
}
score:1
I know this isn't the answer you are looking for, but the easiest path would be to animate the placeholder text instead of the main input text. Then you don't have to worry about anything and can just let the animation play out regardless of the user's actions.
Now the problem I have -- is I need to create an onClick on the textField -- and if its an automated typetext field - reset the value to an empty string -- pass this onclick back up to the parent shells -- and even back up to the typetext function to break the timeout --- so if the user loads the page they see the text typing - but with UI functionality improvements - if I click on the field during mid-animation - I want the animation to stop/break, I want the field to clear.
I want to have control over also which fields should be cleared -- so in this case - have a param - that indicates onClickClear: true -- so as not to break user edit profile pre-filled forms.
All of this is satisfied by using the placeholder of the field instead (although the typetext won't stop because there's no need to as the user's text / prefilled text will hide the placeholders). The only thing I haven't hooked up is to stop the typetext on Home
's componentWillUnmount
as without that, it would throw warning messages that setState is being called on an unmounted component.
I had to do some refactoring as there were some issues with things like mutating React state (toggleFieldVisibility
in FieldMaker.js
) and not updating this.state.fields
when new props are passed down as the state was only being set in the constructor. I also renamed some things within FieldMaker.js
while I was at it (mostly due to personal preference in this case).
There are definitely problems with trying to derive state from props regardless of how you do it: You Probably Don't Need Derived State
Running code:
https://codesandbox.io/s/busy-davinci-mk0dq?file=/src/Home.js
Home.js
state = {
initial_search_term: { search_term: "" },
searchPlaceholder: "",
textPlaceholder: "",
valPlaceholder: ""
};
componentDidMount() {
typeAnimation("Search Text...", 100, (msg) => {
this.setState({
searchPlaceholder: msg
});
});
typeAnimation("Just some super long text you used to know", 100, (msg) => {
this.setState({
textPlaceholder: msg
});
});
typeAnimation("I'm a value, but am I valuable??", 100, (msg) => {
this.setState({
valPlaceholder: msg
});
});
}
// Render funct:
let fieldsSearchForm = [
{
type: "text",
label: "Search Term",
name: ["search_term"],
props: { placeholder: this.state.searchPlaceholder },
options: []
},
{
type: "text",
label: "Text",
name: ["test"],
props: { placeholder: this.state.textPlaceholder },
options: []
},
{
type: "text",
label: "Value",
name: ["test2"],
props: { placeholder: this.state.valPlaceholder }
}
];
FieldMaker.js
The getDerivedStateFromProps is the real main difference here, This is to populate the subs array based on the fields whenever the fields change (and to set visibility). I have no idea how much of that is really necessary as there's no notion of what any of that is actually supposed to do in this. So it likely needs more work to get it fully working.
The other difference is a bit of a refactor to having a separate visiblity
object in state rather than modifying the fields
in state.
The main reason for modifying this file is to make sure that updates to the fields
prop translates to updates to the children Fields
so that the placeholder can be passed down via props to the Field
and thus the renderTextField
state = {
visibility: {}
};
static getDerivedStateFromProps(props, state) {
let newState = { prevFields: props.fields };
if (props.fields !== state.prevFields) {
let visibility = state.visibility;
let subs = props.fields.reduce((subs, field) => {
if (field.sub) {
subs.push(field.sub);
visibility[field.name] = false;
} else {
visibility[field.name] = true;
}
return subs;
}, []);
newState.subs = subs;
}
return newState;
}
toggleFieldVisibility(pos, isVisibile) {
let field = this.props.fields[pos].name;
this.setState((prev) => {
return { ...prev, [field]: isVisibile };
});
// This directly manipulates state, and is likely problematic in React
// let fields = { ...this.state.fields };
// fields[pos]["visibility"] = isVisibile;
}
componentDidMount() {
this.hideSubs();
}
// In render:
return (
<>
{this.props.fields.map((item, j) => {
if (this.state.visibility[item.name]) {
if (item.type !== "checkbox") {
return (
<Field
key={j}
name={item.name[0]}
props={item.props}
label={item.label}
// ...
renderTextField.js
In this, the goal of the change is just to pass the placeholder down to the MUI TextField, and to make the MUI TextField's label shrink back by setting InputLabelProps = {shrink: true}
const renderTextField = ({
input,
rows,
multiline,
label,
type,
meta: { touched, error, warning },
placeholder,
InputLabelProps
}) => {
// Ensure that the label is shrunk to the top of the input
// whenever there's a placeholder set
InputLabelProps = placeholder
? { ...(InputLabelProps ?? {}), shrink: true }
: InputLabelProps;
return (
<FormControl
component="fieldset"
fullWidth={true}
className={multiline === true ? "has-multiline" : null}
>
<TextField
InputLabelProps={InputLabelProps}
placeholder={placeholder}
label={label}
multiline={multiline}
rows={rows}
type={type}
error={touched && (error && error.length > 0 ? true : false)}
helperText={
touched &&
((error && error.length > 0 ? error : null) ||
(warning && warning.length > 0 ? warning : null))
}
{...input}
/>
</FormControl>
);
};
I redid the solution in a very quick and dirty way to avoid the pitfalls that exist in the FieldMaker file that initially caused issues in the original solution:
https://codesandbox.io/s/fervent-moser-0qtvu?file=/src/Home.js
I modified typeAnimation to support a semblance of cancellation by returning a cancel function that stops the looping and sets uses the callback to set the value to the end state.
export function typeAnimation(text, timing, callback) {
let concatStr = "";
let canceled = false;
function cancel() {
canceled = true;
}
async function runAnimation() {
for (const char of text) {
concatStr += char;
await sleep(timing);
if (canceled) {
break;
}
callback(concatStr);
}
if (canceled) {
callback(text);
}
}
runAnimation();
return cancel;
}
Then in Home.js
, I modified intitial state and the componentDidMount to work with the placeholders and give me a location to store the cancel functions.
constructor(props, context) {
super(props, context);
this.state = {
initial_search_term: { search_term: "" },
placeholders: { search_term: "" }
};
}
cancelAnimations = {};
componentDidMount() {
var self = this;
this.cancelAnimations.search_term = typeAnimation(
"Start typing...",
100,
function (msg) {
self.setState((state) => ({
placeholders: { ...state.placeholders, search_term: msg }
}));
}
);
}
I also add fieldsExtras
and pass that all the way down to the FieldMaker component to add extra props to the Field in that component via the index matching the fieldsSearchForm
array.
let fieldsExtras = [
{
placeholder: this.state.placeholders.search_term,
onClick: this.cancelAnimations.search_term
}
];
Then once the extra props have been passed all the way down into the Field, in renderTextField
, I do the same sort of thing as before, but I also added the onClick to call the passed in onClick
function
const renderTextField = ({
input,
rows,
multiline,
label,
type,
meta: { touched, error, warning },
placeholder,
onClick,
InputLabelProps
}) => {
InputLabelProps = placeholder
? { ...(InputLabelProps ?? {}), shrink: true }
: InputLabelProps;
return (
<FormControl
component="fieldset"
fullWidth={true}
className={multiline === true ? "has-multiline" : null}
>
<TextField
placeholder={placeholder}
InputLabelProps={InputLabelProps}
onClick={(e, value) => {
onClick && onClick(e);
}}
label={label}
multiline={multiline}
rows={rows}
type={type}
error={touched && (error && error.length > 0 ? true : false)}
helperText={
touched &&
((error && error.length > 0 ? error : null) ||
(warning && warning.length > 0 ? warning : null))
}
{...input}
/>
</FormControl>
);
};
Source: stackoverflow.com
Related Query
- reactjs - redux form and material ui framework -- with auto type - and clearing field functionality
- Reactjs - Material UI - redux form framework - with password toggle - not using hooks
- Django validation with ReactJS and Redux Form
- With redux form how can I normalize and allow only positive integers on an input type number?
- How to connect simple Formik form with Redux store and dispatch an action?
- Auto save form fields in react and redux
- Multiple Registration Form with redux and react
- using Material-ui checkboxes with the reactjs and redux
- Reactjs - Material UI- reduc form framework - grouped checkbox required validation error fixing to have at least one checkbox required
- How to add login authentication and session to ReactJS React Router and Redux with MongoDB + NodeJS Express?
- Returning textfield value with material ui and reactjs
- Form submit and validation with MaterialUI & ReactJS
- React hook form with redux and async request
- ReactJS - Proper way to fetch data from redux and load form
- Initialize form values with redux form initialize function and a GET request
- An invalid form control with name='' is not focusable error Material ui input type file
- Nested sidebar menu with material ui and Reactjs
- How do i prevent circular type reference in typescript with redux and connect when using mapState function?
- error to read data with redux and reactJs
- How to disable entire redux form after submitting and then re-enable it after clearing all fields
- Material ui Text Field and Formik form with custom compnents
- Add autocomplete with multiple and creatable reactjs material ui
- ReactJS Dynamic Form Validation with Material UI
- Redux Form Material UI: Select with Nested Lists not working
- React Redux form 2 submits with onSubmit and redirect
- How to integrate .css and .js with ReactJS + Redux architecture?
- How to Create Middleware for refresh token in Reactjs with axios and redux
- How to create contact form with ReactJS and send the values with API - Express
- How to define component instance attributes with ReactJS and Typescript? `Property 'var' does not exist on type 'App'`
- Can't Edit and Update properties with form Reactjs and MongoDB
More Query from same tag
- how to define a function type prop in enzyme?
- Nesting a fetch promise inside another promise (React)?
- Redux using functional component without React
- Passing the user session from express to React state
- React JS Crud Issues with mutable data
- Component constantly re-rendering when dispatching action (promise)
- Can I customize Carousel-indicators in react-responsive-carousel
- Validate multiple fields with different index using react hook form
- Check if a div element can receive paste event i.e if it is focused
- Serving a Create-React-App application with a Node/Express router module
- Why is the app not re-rendering even if the state/dependency has changed
- Using onBlur with JSX and React
- Why are props being modified when clicking on a route?
- How to type a React.ComponentType with a static property?
- Is there a better way to split the reducers to smaller pieces?
- How to use static information in react app?
- Posting with axios keeps returning Request failed with status code 422
- How can I convert a function into a class in React?
- want to increase previous and next page pagination icon size in material react table
- Meteor & React | Login Logout navigation
- Why can react-test-renderer only findByType functional components in the same file in React?
- i want to retrieve location from promise aaray of open cage geocoding by passing lat and log but dont know how to retrieve the value
- How can I see the content from react pages? It only shows the files
- How do I convert image type In React- webp to png or jpg?
- Typing a letter in the middle of a string takes the cursor back to the end - Material UI
- State is returning new state without touching the state REACT/REDUX
- Cannot add link and label inside an input element in reactjs
- Multiple React versions in a monorepo, is it possible?
- How can I print all elements of an array (passed as props from the parent's state) inside JSX code?
- For loop inside ComponentDidMount to iterate through an object not working