Problem converting state in class to function in react - javascript

I have been converting all my classes to functions to update my react projects. I am getting a:
'ESLint has encountered a parsing error' in my code. If I remove ONE line, the error disappears. Here's the code
import React, { useState } from "react";
import orderBy from "lodash/orderBy";
import SelectField from "#material-ui/core";
import MenuItem from "#material-ui/core";
import TextField from "#material-ui/core";
import Table from "./Table";
const invertDirection = {
asc: "desc",
desc: "asc"
};
const Patients = props =>
{
var handleRemove = (i) => {
setPatientsData(patientsData.filter((row, j) => j !== i));
};
var handleSelect = (i) =>
{
setEditIdx(i);
}
const [editIdx, setEditIdx] = useState(-1);
const [columnToSort, setColumnToSort] = useState('');
const [sortDirection, setSortDirection] = useState('asc');
const [patientsData, setPatientsData] = useState([]);
const [columnToQuery, setColumnToQuery] = useState('');
const [columnQueryValue, setColumnQueryValue] = useState('');
var handleSort = columnName =>
{
if (columnName === columnToSort) {
setSortDirection(invertDirection[sortDirection]);
}
else {
setSortDirection("asc");
}
setColumnToSort(columnName);
};
var handleQueryChange = (value) =>
{
setColumnQueryValue({ value });
}
const lowerCaseQuery = this.state.query.toLowerCase();
return (
<div>
<div style={{ display: "flex" }}>
<div style={{ display: "flex", margin: "auto" }}>
<TextField
hintText="Query"
floatingLabelText="Query"
value={columnQueryValue}
onChange={(event) => setColumnQueryValue({event.target.value })} --->>>ERROR
floatingLabelFixed
/>
<SelectField
style={{ marginLeft: "1em" }}
floatingLabelText="Select a column"
value={columnToQuery}
onChange={(event, index, value) => setColumnToQuery({value }) }
>
<MenuItem value="firstName" primaryText="First Name" />
<MenuItem value="lastName" primaryText="Last Name" />
<MenuItem value="username" primaryText="Username" />
<MenuItem value="email" primaryText="Email" />
</SelectField>
</div>
</div>
<Table
handleSort={handleSort}
handleRemove={handleRemove}
editIdx={editIdx}
handleSelect={handleSelect}
columnToSort={columnToSort}
sortDirection={sortDirection}
data={orderBy(
columnQueryValue
? patientsData.filter(x =>
x[columnToQuery]
.toLowerCase()
.includes(lowerCaseQuery)
)
: patientsData,
columnToSort,
sortDirection
)}
header={[
{
name: "First name",
prop: "firstName"
},
{
name: "Last name",
prop: "lastName"
},
{
name: "Username",
prop: "username"
},
{
name: "Email",
prop: "email"
}
]}
/>
</div>
);
}
export default Patients;
Specifically, this line:
onChange={(event) => setColumnQueryValue({event.target.value })}
I've spent 24 hours trying to fix it, tried everything, but still get the error. Any help would be greatly appreciated!
ADDED: I fixed a typo that was not causing the problem. Specifically, the interpreter complains about the 'event.target.value' I am passing to the setColumnQueryValue which is the state setter for ColumnQueryValue, which is a string. Which is SUPPOSED to be what a text field returns. (event.target.value). That is the code that is the issue and that I cannot figure out why it's complaining.

Firstly, You don't have any setQueryValue function in your code, I think you misinterpreted with setColumnQueryValue. Secondly, you're passing an object and didn't define a key for the event.target.value. As I see that you initialized columnQueryValue with empty string so you should pass a string value in function.
onChange={(event) => setColumnQueryValue(event.target.value)}

Ok, well separating the onChange event into a called function made the error go away, but I still don't understand what I did wrong.
Added:
var handleOnChange = event =>
{
setColumnQueryValue(event.target.value);
};
Changed:
onChange={(event) => setColumnQueryValue({event.target.value })}
to
onChange={handleOnChange}
No difference I can detect in the actual code, but one worked, the other didn't. I hate javascript. ;-)

Related

Using react-number-format: cannot type more than one symbol at once

I am using react-number-format package inside of my TextField (material-ui). It is having strange behavior and not letting me put more than one symbol at once in the TextField. When I start typing after first click the Field is not focused anymore. I have used the same thing in other projects and it is working fine but here I cannot see from where the problem is coming. Here is the sandbox:
https://codesandbox.io/s/little-cherry-ubcjv?file=/src/App.js
import React,{useState} from 'react'
import { TextField} from "#material-ui/core";
import NumberFormat from "react-number-format";
export default function App() {
const [minAssets, setMinAssets] = useState();
const NumberFormatCustom = (props) => {
const { inputRef, onChange, ...other } = props;
return (
<NumberFormat
{...other}
getInputRef={inputRef}
onValueChange={(values) => {
onChange({
target: {
name: props.name,
value: values.value,
},
});
}}
thousandSeparator
isNumericString
prefix="$"
/>
);
};
return (
<div className="App">
<TextField
label="Min Assets"
value={minAssets}
onChange={(e) => setMinAssets(e.target.value)}
name="minAssets"
variant="outlined"
id="Minimum-Income-filter"
InputProps={{
inputComponent: NumberFormatCustom,
}}
/>
</div>
);
}
In your case you don't really need the onValueChange prop on the NumberFormat component since you already have an onChange on the TextField component that update the minAssets state.
So you can directly use this minAssets as the value of the prop value from NumberFormat like:
return (
<NumberFormat
{...other}
value={minAssets}
getInputRef={inputRef}
thousandSeparator
isNumericString
prefix="$"
/>
);

How to add rows dynamically in ReactJS?

I'm still beginner to ReactJS and need to build a dynamic table for my work.
In that table, the user can add new lines and can also remove any existing lines.
The problem is, I don't know how to save the values that are typed into the new fields. My onChange function isn't working, I've done several tests, but I'm not able to save the entered values.
Here's my code I put into codesandbox.
Could you tell me what I'm doing wrong to save the entered values? Thank you in advance.
import React from "react";
import "./styles.css";
import List from "./List/List";
const App = () => {
const [data, setData] = React.useState([
[
{
label: "Property Name",
field: "propertyName",
value: ""
},
{
label: "Value",
field: "value",
value: ""
}
]
]);
const handleOnChange = (field) => (e) => {
setData((prev) => ({
...prev,
[field]: e.target.value
}));
};
const addRow = () => {
setData([
...data,
[
{
label: "Property Name",
field: "propertyName",
value: ""
},
{
label: "Value",
field: "value",
value: ""
}
]
]);
};
const removeRow = (index) => {
const _data = [...data];
_data.splice(index, 1);
setData(_data);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<List
data={data}
addRow={addRow}
removeRow={removeRow}
handleOnChange={handleOnChange}
/>
</div>
);
};
export default App;
import React from "react";
import AddCircleIcon from "#material-ui/icons/AddCircle";
import RemoveCircleIcon from "#material-ui/icons/RemoveCircle";
import TextField from "#material-ui/core/TextField";
import "./styles.scss";
const List = ({ data, handleOnChange, addRow, removeRow }) => {
return (
<div className="container">
{data.map((items, index) => (
<div key={index} className="content">
<div className="content-row">
{items.map((item, index) => (
<TextField
key={index}
label={item.label}
value={item.value}
onChange={handleOnChange(index)}
variant="outlined"
/>
))}
</div>
<div>
<AddCircleIcon onClick={addRow} />
{data.length > 1 && (
<RemoveCircleIcon onClick={() => removeRow(index)} />
)}
</div>
</div>
))}
</div>
);
};
export default List;
Good that you shared the code. There are several issues with your code. I have an updated code placed under this URL,
https://codesandbox.io/s/reverent-mclean-hfyzs
Below are the problems,
Your data structure is an array of arrays and your onchange event doesn't respect that.
You have no property available [name/id] to identify a textbox when you change the value.
I had to add a name property to each textbox and design it like a 2D array so any textbox will have a unique name.
I had to map through the data array and find the node where I have to update the value and set the new value as the new state when any textbox changes.
I have added a console.log while adding a row so you can see the current state.

How properly connect react-final-form and material-ui-chip-input?

I'm using material-ui-chip-input wrapped with Field react-final-form.
I wanted to limit "CHIPS" to 5 only.
Chips are compact elements that represent an input, attribute, or
action.
material ui docs
As you can see I'm using 2 states here
react useStates
react-final-form internal states
This is redundant because react-final-form handled states internally but I can't make to work I'm just showing what I have done so far.
Basically there are
two problems with my implementation.
It doesn't limit my chips.
My react-final-form field values - not updating when clicking DeleteChip
import ChipInput from 'material-ui-chip-input'
import { Field } from 'react-final-form'
const [state, setState] = useState([])
const AddChip = useCallback(
(chip) => {
if (state.length + 1 <= 5) {
setState((prev) => ([...prev.tags, chip]))
}
},
[setState, state.length]
)
const DeleteChip = useCallback(
(chip) => {
setState((prev) => (...prev.state.filter((p) => p !== chip)]
}))
},
[setState]
)
return (
<Field name="states" validate={isRequired} >
{({ input: { value, onChange, ...rest }, meta }) => {
<ChipInput
defaultValue={Array.isArray(value) ? value : []} // check value first because material-ui-chip-input require an array, by default react-final-form value is empty string
onChange={(event) => { // uncontrolled
AddChip(event)
onChange(event)
// I tried below code but same result not working
// if (state.length + 1 <= 5) {
// onChange(event)
// }
}}
onDelete={DeleteChip}
/>
}}
</Field>
)
material-ui-chip-input
react-final-form
see codesandbox demo
This is my take:
https://codesandbox.io/s/proud-water-xp2y1?file=/src/App.js
Key points:
Don't duplicate the state, let react final form handle the state for you
Pass an empty array as the initial state to the FORM, don't pass defaultValues to the Field.
according to the material-ui-chip-input package you need to use onAdd if used in controlled mode, which we do, since we let react final form handle the state.
Add the value prop to the Chipinput.
For cosmetic reasons: don't actually use the render-prop inside <Form /> - use a functional child component instead.
Code:
import ChipInput from "material-ui-chip-input";
import { Form, Field } from "react-final-form";
export default function App() {
return (
<Form
initialValues={{
states: []
}}
onSubmit={() => console.log("submitted")}
>
{({ values, onSubmit }) => (
<form onSubmit={onSubmit}>
<Field name="states">
{({ input: { value, onChange } }) => (
<ChipInput
value={value}
alwaysShowPlaceholder={true}
placeholder="type states here"
onAdd={(newVal) => {
if (value.length >= 5) return;
const newArr = [...value, newVal];
onChange(newArr);
}}
onDelete={(deletedVal) => {
const newArr = value.filter((state) => state !== deletedVal);
onChange(newArr);
}}
/>
)}
</Field>
<p>react useStates</p>
<p>react-final-form values</p>
<pre
style={{
backgroundColor: "rgba(0,0,0,0.1)",
padding: "20px"
}}
>
{JSON.stringify(values, 0, 2)}
</pre>
</form>
)}
</Form>
);
}

Material-ui radio button not working together with react-final-form

My material-ui radio button was originally working but when I implemented with react-final-form I can make to work. The thing now is that radio button not clickable at all, but when I remove the ...input inside inputProps radio button works.
I know there's a material-ul react-final-form wrapper https://github.com/Deadly0/final-form-material-ui but I don't want to use it at the moment.
Any help there?
import { Box, Typography } from '#material-ui/core'
import { useCallback, useState } from 'react'
import { useContext } from '/form-context'
import { Field } from 'react-final-form'
const GetFruitFav = () => {
const { values, setValues } = useContext()
const { favFruits = 'orange' } = values || {}
const [food, setFood] = useState(favFruits)
const handleBtn = useCallback(
(value) => {
setFood(value)
},
[setFood]
)
return (
<div>
<Field
name="food"
type="radio"
render={({ input, meta }) => (
<StyledRadioBox>
<StyledRadioButton
checked={food === 'orange'}
onChange={() => handleBtn('orange')}
value="orange"
name="orange"
inputProps={{
'aria-label': 'orange',
...input,
}}
/>
</StyledRadioBox>
)}
/>
<Field
name="food"
type="radio"
render={({ input, meta }) => (
<>
<StyledRadioBox>
<StyledRadioButton
checked={food === 'grapes'}
onChange={() => handleBtn('grapes')}
value="grapes"
name="grapes"
inputProps={{
'aria-label': 'grapes',
...input,
}}
/>
</StyledRadioBox>
</>
)}
/>
</div>
)
}
So basically, what I wanted are when user clicked on material-ui radio button this will save to my react context at the same time react-final-form will save the values too.
I solved this problem, by refactoring my code
<Field
name = "food"
value = "orange"
type = "radio"
render = { ({ input: { checked, onChange, value, ..rest}, meta }) => (
<StyledRadioBox >
<StyledRadioButton checked = {checked}
// let react-final-form handle this not material-ui
onChange = {
(event) => {
onChange(event)
handleBtn(value)
// uncontrolled let react-final-form handle the values instead of adding manually
}
}
inputProps = {
{
'aria-label': 'orange',
value,
...rest,
}
}
/>
</StyledRadioBox>
)
}
/>

Invalid hook call when I'm setting values from numpad using useChange

This is my component:
const pricePicker = ({
step,
precision,
input,
placeholder,
label,
theme,
props,
meta: { touched, error },
...rest
}) => {
/*In the FOLLOWING LINES from "function useChange(e)" to "return [value,change]"
is the error known as Invalid hook.
*/
function useChange(e){
const [value,setValue] = useState(0);
function change(event){
setValue(value => event.target.value);
}
return [value,change];
}
const handleBlur = (e) => {
if (e.target.value === '0') e.target.value = '0'
}
const handleKeypress = (e) => {
const characterCode = e.key
if (characterCode === 'Backspace') return
const characterNumber = Number(characterCode)
if (characterNumber < 0) {
e.preventDefault()
}
}
const myTheme = {
fontFamily: 'Arial',
textAlign: 'center',
header: {
primaryColor: '#263238',
secondaryColor: '#f9f9f9',
highlightColor: '#FFC107',
backgroundColor: '#607D8B',
},
body: {
primaryColor: '#263238',
secondaryColor: '#32a5f2',
highlightColor: '#FFC107',
backgroundColor: '#f9f9f9',
},
panel: {
backgroundColor: '#CFD8DC'
}
};
return(
<div className='form-group'>
<label forname={input.name}>{label}</label> <br />
<NumPad.Number
{...rest}
step={0.1}
precision={2}
placeholder={!input.value ? 'Please, type a number' : input.value}
selected={input.value ? new NumPad.Number(input.value) : null}
onKeyDown={(changedVal) => handleKeypress(changedVal)}
onBlur={(changedVal) => handleBlur(changedVal)}
onChange={(changedVal) => useChange(changedVal)}
className='form-control'
/>
<div className='text-danger' style={{ marginBottom: '20px' }}>
{touched && error}
</div>
</div>
);
};
export default pricePicker;
When I'm executing this block of code:
function useChange(e){
const [value,setValue] = useState(0);
function change(event){
setValue(value => event.target.value);
}
return [value,change];
}
I'm getting the following issue:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I've tried all ways but it seems it's impossible. I never used hooks and previously I post about something similar but unsuccesfully. Previous post talks about useState is inside pricePicker function is neither a funcional component or react hook component when executed previous code lines like this:
const handleChange = (e) =>{
// in the following line is the error.
const[value, setValue] = useState(0);
}
How can I solve this issue? I need to fix it, but how? I've tried all ways but unsuccessfully.
Any one knows how can I fix this issue? It's important.
The error is actually quite simple - hooks can be used only at the top level of functional components. In your concrete example, you cannot use useState inside of function useChange.
Instead, do something like:
const pricePicker = ({/*props go here*/
const [value,setValue] = useState(0);
// handler of input change
const onChange = e => setValue(e.target.value);
// the rest of the code can stay the same
return <div className='form-group'>
<label forname={input.name}>{label}</label> <br />
<NumPad.Number
{...rest}
step={0.1}
precision={2}
placeholder={!input.value ? 'Please, type a number' : input.value}
selected={input.value ? new NumPad.Number(input.value) : null}
onKeyDown={(changedVal) => handleKeypress(changedVal)}
onBlur={(changedVal) => handleBlur(changedVal)}
onChange={onChange} /* Here's we use the onChange handler */
className='form-control'
/>
<div className='text-danger' style={{ marginBottom: '20px' }}>
{touched && error}
</div>
</div>;
}

Categories