ReactJs Show state value in TextInput - reactjs

I am working on a form where the user must fill many fields.
Some of those fields have data that comes from entities, so I created a search component where the user clicks on a button, a grid of the entity opens and when selecting I return the selected row as a prop and save in states the values. (this works ok)
Those values that are stored in States, to be shown in diferents TextInput (react-admin)
import React, {useState, useCallback} from "react";
import {Edit, TextInput, SimpleForm} from 'react-admin';
import ProcesoSearchButton from "../proceso/ProcesoBusquedaBtn"
const ProcesoEdit = props => {
const [state, setState] = useState({});
const classes = useStyles();
const {
idProceso = '', proceso = '', d_proceso = '',
} = state;
const updateProceso = useCallback(async (who) => {
setState(state => ({
...state,
idProceso: who.id,
proceso: who.proceso,
d_proceso: who.d_proceso,
}));
})
return (
<Edit {...props} title={<ProcesoStdTitle/>}>
<SimpleForm>
<TextInput source="proceso" label={"N°Proceso"}
defaulValue={proceso}
fullWidth={true}
formClassName={classes.proceso} inputProps={{readOnly: true,}} variant="filled"/>
<TextInput source="d_proceso" label={"Descripción"} fullWidth={true}
defaulValue ={d_proceso}
formClassName={classes.d_proceso} inputProps={{readOnly: true,}}
variant="filled"/>
<ProcesoSearchButton callbackProceso={updateProceso} formClassName={classes.btnBusqueda}/>
....
</SimpleForm>
</Edit>
)
};
export default ProcesoEdit;
With the previously detailed code it works in a first search, States are updated and shown in the TextFields. But if i do a second search, the States are updated but not the displayed value in TextFields.
If I use the value prop it doesn't work.
How can I solve that? I am trying to keep using react-admin because it's what I use throughout the app. This code using MUI TextField works, but it would not be ideal. Thanks

Related

React Hooks: State Is Not Updating in Function

I have spent several hours trying to fix this issue with no luck. I have a reusable component that starts with a state of any empty object, the object is given a series of properties based off of props, and then based on user input on each set of radio buttons will show a child component.
Unfortunately, the function that is supposed to update the state that will then trigger whether the user sees the child component is not working.
I believe the issue is in my editDisplay function. Currently when I click one of the radio buttons the new object that should become the new state is logged to the console correctly, but when I use React Devtools to inspect the page, I see that state is not updating. That being said, I could see a case where I am misunderstanding useEffect, and perhaps useEffect is running each time editDisplay is running. Any help would be appreciated.
import React, {useState, useEffect} from 'react'
import {Form} from 'react-bootstrap'
import RatingInput from './RatingInput'
export default function MembershipForm({inputs, name, record, setRecord}) {
const [display, setDisplay] = useState({})
useEffect(()=>{
inputs.forEach(input => {
var newDisplay = display
newDisplay[name+input]=false
setDisplay(newDisplay)})
},[])
const editDisplay = (input, visible) => {
var newDisplay =display
newDisplay[name+input]=visible
console.log(newDisplay)
setDisplay(newDisplay)
}
const questions=inputs.map(input => {
return (
<div key={name+input}>
<Form.Group>
<Form.Label className='mx-2'>Do you have a {name} {input} Rating?</Form.Label>
<Form.Check inline label='Yes' value={true} name={name+input} type='radio' className = 'mx-2' onClick ={() =>{editDisplay(input, true)}} />
<Form.Check inline label='No' value ={false} name={name+input} type='radio' className = 'mx-2' defaultChecked onClick = {() =>{editDisplay(input, false)}} />
</Form.Group>
{display[name+input] && <RatingInput input={input}/>}
</div >
)
})
return (
<div className='ml-3'>
{questions}
</div>
)
}
const editDisplay = (input, visible) => {
var newDisplay = display
newDisplay[name+input]=visible
console.log(newDisplay)
setDisplay(newDisplay)
}
You should perform react state update in an immutable way.
What you are doing above is you are mutating existing state variable (that assignment on first line doesn't add much: newDisplay refers to same object as display). In such case react might not detect change. Do this instead:
var newDisplay = { ... display };

React Hooks with Styled Components - Form not working

So I'm using React Hooks with styled components, I have tried to style a form but when I make it into a styled component the form doesn't work ie you type one letter then the form looses focus, you have to click back into the input box, type one letter and it looses focus again etc...
I'm also getting this warning in the dev tools but I don't really understand what I need to do -
index.js:27 The component styled.form with the id of "sc-eCssSg" has been created dynamically.
You may see this warning because you've called styled inside another component.
To resolve this only create new StyledComponents outside of any render method and function component.
at LocationForm (https://3deis.csb.app/src/Components/LocationForm.js:31:39)
at div
at App (https://3deis.csb.app/src/App.js:53:39)
How do I change the below code to do what is needed to make it work ?
import React, { useState } from "react";
import axios from "axios";
import styled from "styled-components";
const LocationForm = (props) => {
const [locationName, setName] = useState("");
const Form = styled.form``;
const handleSubmit = (evt) => {
evt.preventDefault();
axios
.get(
`http://www.mapquestapi.com/geocoding/v1/address?key=z2G40AM2VSDfXx7MQtCqAvmXmoYEX8cV&location=${locationName}&maxResults=1`
)
.then((res) => {
const latitude = res.data.results[0].locations[0].displayLatLng.lat;
const longitude = res.data.results[0].locations[0].displayLatLng.lng;
const city = res.data.results[0].locations[0].adminArea5;
// const submitted = !true;
props.callbackFromParent(
locationName,
// submitted,
latitude,
longitude,
city
);
})
.catch((error) => {
console.log(error);
});
};
return (
<Form onSubmit={handleSubmit}>
<label>
Location:
<input
type="text"
value={locationName}
onChange={(e) => setName(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</Form>
);
};
export default LocationForm;
It says exactly what is happening. You are creating const Form = styled.form inside your LocationForm render function. If you move it 4 lines up outsie the function it will stop giving the warning. In general you should never create a styled component inside a render function, because it will recreate the form each render (so each time you input a character) instead of only once upon initialization.

useState hook in context resets unfocuses input box

My project takes in a display name that I want to save in a context for use by future components and when posting to the database. So, I have an onChange function that sets the name in the context, but when it does set the name, it gets rid of focus from the input box. This makes it so you can only type in the display name one letter at a time. The state is updating and there is a useEffect that adds it to local storage. I have taken that code out and it doesn't seem to affect whether or not this works.
There is more than one input box, so the auto focus property won't work. I have tried using the .focus() method, but since the Set part of useState doesn't happen right away, that hasn't worked. I tried making it a controlled input by setting the value in the onChange function with no changes to the issue. Other answers to similar questions had other issues in their code that prevented it from working.
Component:
import React, { useContext } from 'react';
import { ParticipantContext } from '../../../contexts/ParticipantContext';
const Component = () => {
const { participant, SetParticipantName } = useContext(ParticipantContext);
const DisplayNameChange = (e) => {
SetParticipantName(e.target.value);
}
return (
<div className='inputBoxParent'>
<input
type="text"
placeholder="Display Name"
className='inputBox'
onChange={DisplayNameChange}
defaultValue={participant.name || ''} />
</div>
)
}
export default Component;
Context:
import React, { createContext, useState, useEffect } from 'react';
export const ParticipantContext = createContext();
const ParticipantContextProvider = (props) => {
const [participant, SetParticipant] = useState(() => {
return GetLocalData('participant',
{
name: '',
avatar: {
name: 'square',
imgURL: 'square.png'
}
});
});
const SetParticipantName = (name) => {
SetParticipant({ ...participant, name });
}
useEffect(() => {
if (participant.name) {
localStorage.setItem('participant', JSON.stringify(participant))
}
}, [participant])
return (
<ParticipantContext.Provider value={{ participant, SetParticipant, SetParticipantName }}>
{ props.children }
</ParticipantContext.Provider>
);
}
export default ParticipantContextProvider;
Parent of Component:
import React from 'react'
import ParticipantContextProvider from './ParticipantContext';
import Component from '../components/Component';
const ParentOfComponent = () => {
return (
<ParticipantContextProvider>
<Component />
</ParticipantContextProvider>
);
}
export default ParentOfComponent;
This is my first post, so please let me know if you need additional information about the problem. Thank you in advance for any assistance you can provide.
What is most likely happening here is that the context change is triggering an unmount and remount of your input component.
A few ideas off the top of my head:
Try passing props directly through the context provider:
// this
<ParticipantContext.Provider
value={{ participant, SetParticipant, SetParticipantName }}
{...props}
/>
// instead of this
<ParticipantContext.Provider
value={{ participant, SetParticipant, SetParticipantName }}
>
{ props.children }
</ParticipantContext.Provider>
I'm not sure this will make any difference—I'd have to think about it—but it's possible that the way you have it (with { props.children } as a child of the context provider) is causing unnecessary re-renders.
If that doesn't fix it, I have a few other ideas:
Update context on blur instead of on change. This would avoid the context triggering a unmount/remount issue, but might be problematic if your field gets auto-filled by a user's browser.
Another possibility to consider would be whether you could keep it in component state until unmount, and set context via an effect cleanup:
const [name, setName] = useState('');
useEffect(() => () => SetParticipant({ ...participant, name }), [])
<input value={name} onChange={(e) => setName(e.target.value)} />
You might also consider setting up a hook that reads/writes to storage instead of using context:
const useDisplayName = () => {
const [participant, setParticipant] = useState(JSON.parse(localStorage.getItem('participant') || {}));
const updateName = newName => localStorage.setItem('participant', {...participant, name} );
return [name, updateName];
}
Then your input component (and others) could get and set the name without context:
const [name, setName] = useDisplayName();
<input value={name} onChange={(e) => setName(e.target.value)} />

How to connect redux store using useSelector() when input fields already mapped using useState()

I am playing around with the new React-Redux Hooks library
I have an react component that has two input fields that update to the react store using useState() - desc and amount. In order to update changes to the the redux store when field has been edited I use onBlur event and call dispatch to the redux store. That works fine.
When I want to clear the fields from another component I would like this to work in same manner as for class based functions via connect & map State to Props, however to to this with functional component I need to utilise useSelector(). I cannot do this as the identifiers desc and amount are already used by useState()
What am I missing here?
import { useDispatch, useSelector } from "react-redux"
import { defineItem, clearItem } from "../store/actions"
const ItemDef = props => {
const dispatch = useDispatch()
const [desc, setDesc] = useState(itemDef.desc)
const [amount, setAmount] = useState(itemDef.amount)
//MAPSTATETOPROPS
//I WANT TO HAVE THESE VALUES UPDATED WHEN REDUX STORE CHANGES FROM ANOTHER COMPONENT
//THESE LINES WILL CAUSE ERROR to effect - identifier has already been declared
const desc = useSelector(state => state.pendingItem.desc)
const amount = useSelector(state => state.pendingItem.amount)
return (
<div>
<p>Define new items to be added below - before clicking Add Item</p>
<input
value={desc}
type="text"
name="desc"
placeholder="Description of Item"
onChange={e => setDesc(e.target.value)}
//Use onBlur Event so that changes are only submitted to store when field loses focus
onBlur={e => dispatch(defineItem(desc, amount))}
/>
<input
value={amount}
type="number"
name="amount"
placeholder="Amount"
onChange={e => setAmount(e.target.value)}
//Use onBlur Event so that changes are only submitted to store when field loses focus
onBlur={e => {
dispatch(defineItem(desc, amount))
}}
/>
</div>
)
}
export default ItemDef
SOLUTION - WITH FULL CODE IN REPOSITORY
I worked out a solution by using useSelector (to map pendingItem part of redux state to itemDef) and the setEffect hook to apply useState to either state item (from input) or itemDef (from Redux State - this happens when redux is updated by another component or through the ADD ITEM TO INPUT button)
I have posted the working component below. I have also posted this small application to demonstrate how to use reacdt-redux libraries with both class based components and fuinctional components using hooks
The repository is https://github.com/Intelliflex/hiresystem
//**************************************************************************************************
//***** ITEMDEF COMPONENT - Allow entry of new Items (dispatched from button in HireList Table) ****
//**************************************************************************************************
import React, { useState, useEffect, useRef } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { defineItem, clearItem } from '../store/actions'
import _ from 'lodash'
const ItemDef = props => {
//BRING IN DISPATCH FROM REDUX STORE
const dispatch = useDispatch()
//DEFINE SELECTOR - EQUIV TO MAPSTATETOPROPS
const { itemDef } = useSelector(state => ({
itemDef: state.pendingItem
}))
const [item, setItem] = useState({ desc: '', amount: 0 })
const onChange = e => {
setItem({
...item,
[e.target.name]: e.target.value
})
}
const prevItem = useRef(item)
useEffect(() => {
//WE NEED TO CONDITIONALLY UPDATE BASED ON EITHER STORE BEING CHANGED DIRECTLY OR INPUT FORM CHANGING
if (!_.isEqual(item, prevItem.current)) {
//INPUT HAS CHANGED
setItem(item)
} else if (!_.isEqual(item, itemDef)) {
//REDUX STATE HAS CHANGED
setItem(itemDef)
}
prevItem.current = item
}, [item, itemDef]) //Note: item and ItemDef are passed in as second argument in order to use setItem
const clearIt = e => {
dispatch(clearItem())
}
const addIt = e => {
dispatch(defineItem({ desc: 'MY NEW ITEM', amount: 222 }))
}
return (
<div>
<p>Define new items to be added below - before clicking Add Item</p>
<input
value={item.desc}
type='text'
name='desc'
placeholder='Description of Item'
onChange={onChange}
//Use onBlur Event so that changes are only submitted to store when field loses focus
onBlur={e => dispatch(defineItem(item))}
/>
<input
value={item.amount}
type='number'
name='amount'
placeholder='Amount'
onChange={onChange}
//Use onBlur Event so that changes are only submitted to store when field loses focus
onBlur={e => dispatch(defineItem(item))}
/>
<button onClick={clearIt}>CLEAR ITEM</button>
<button onClick={addIt}>ADD ITEM TO INPUT</button>
</div>
)
}
export default ItemDef

How to Manipulate Dropdown placeholder, onFocus?

Am new to ReactJS. I need to make the "placeholder" which is set to "State" initially to Empty/Null when onClicked or onFocus and then when it's not focused on, it goes back to "State" again. Can someone help me with this, am very new to react so any help will be appreciated.
import React from "react";
import { render } from "react-dom";
import { Container, Button, Modal, Dropdown } from "semantic-ui-react";
const stateOptions = [
{ key: "AL", value: "AL", text: "Alabama" },
{ key: "NY", value: "NY", text: "New York" }
];
const App = () => (
<Dropdown
placeholder="State"
fluid
multiple
search
selection
options={stateOptions}
/>
);
render(<App />, document.getElementById("root"));
From React's perspective, placeholder is a state that needs to be changed according to user's actions (onClick, onBlur)
So create a state to hold placeholder value that need to change.
There are two ways (since v16.8.0 with the introduction of React Hooks).
Using Class Component
class DropDown extends React.Component {
defaultPlaceholderState = "State";
state = { placeholder: this.defaultPlaceholderState };
clearPlaceholder = () => this.setState({ placeholder: "" });
resetPlaceholder = () =>
this.setState({ placeholder: this.defaultPlaceholderState });
render() {
return (
<Dropdown
onClick={this.clearPlaceholder}
onFocus={this.clearPlaceholder}
onBlur={this.resetPlaceholder}
placeholder={this.state.placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
}
}
In the code above, placeholder declared as a state with default value set to this.defaultPlaceholderState.
When a user clicks on the dropdown, onClick clears the placeholder value by setting it to an empty string. Same for onFocus when the Dropdown is on focus.
When a user clicks outside (onBlur), resetPlaceHolder sets the placeholder value to the default this.defaultPlaceholderState.
Using Function Component with useState hook
React v16.8.0 introduces Hooks, which enables Function Components (not a Functional Component, as it refers to Functional Programming) to hold states.
You can use React.useState hook to hold placeholder value.
const DropDownUsingHook = () => {
const defaultPlaceholderState = "State";
const [placeholder, setPlaceholder] = React.useState(defaultPlaceholderState);
const clearPlaceholder = () => setPlaceholder("");
const resetPlaceholder = () => setPlaceholder(defaultPlaceholderState);
return (
<Dropdown
onClick={clearPlaceholder}
onFocus={clearPlaceholder}
onBlur={resetPlaceholder}
placeholder={placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
};
⚠ Note: Unlike the Class version, clearPlaceholder, resetPlaceholder methods and placeholder state don't use this. prefix.
The implementation is similar but you use useState hook to declare the state and the setter (setPlaceholder).
Refer to the Hooks documentation, Using State Hook for more info.
You can play around with the working code on CodeSandbox.

Resources