I want to make my class more reusable. I have huge form to handle, and I don't want write single method for each input. How to pass state value as parameter to method?
I was trying:
state = {
subtitle: ""
};
inputHandler = (e, param) => this.setState({ [param]: e.target.value });
render() {
return (
<>
{this.state.title}
<input
type="text"
value={this.state.subtitle}
onChange={e => this.inputHandler(this.state.subtitle)}
/>
</>
);
}
and different similar combinations of this solution.
Demo: https://codesandbox.io/s/kw6pnxwv0v
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
state = {
subtitle: ""
};
onChange = (e) => {
const target = e.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<>
{this.state.title}
<input
type="text"
name="subtitle"
value={this.state.subtitle}
onChange={this.onChange}
/>
{this.state.subtitle}
</>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Let me know if this helps :)
Related
I need to update the value of Form.Item manually. I have a custom component, which returns selected value, and want to pass this value to the Form for validation and so I can send it.
Is this possible with antd?
Here's the simplified code:
import { Form } from "antd";
import { FC, ReactElement, useEffect, useState } from "react";
const Child: FC<{
returnValue: (value: any) => void;
}> = ({ returnValue }): ReactElement => {
return <input onChange={(e) => returnValue(e.currentTarget.value)} />;
};
export default function App() {
const { useForm } = Form;
const [form] = useForm();
const [value, setValue] = useState<string>("");
const setNewValue = (newVal: string) => setValue(newVal);
useEffect(() => {
console.log(value);
}, [value]);
return (
<div className="App">
test
<Form form={form}>
<Form.Item
//value={value} I'm looking form smth like this
>
<Child returnValue={setNewValue} />
</Form.Item>
</Form>
</div>
);
}
And here is the code sandbox.
Using <Input> from antd will not work in my case. This is a simplified problem. I have a way more complex component, which behaves in a similar way. The returnValue is how I managed to pull the value out of the component.
For a class based component, this is how you would define a form
class CustomForm extends React.Component {
formRef = React.createRef();
constructor()
render(){
return(
<Form
ref={this.formRef}
name="customForm"
>
<Form.Item label="Email" name="email">
<Input />
</Form.Item>
</Form>
)}
}
and this is how you set form.items value
componentDidUpdate(){
this.formRef.current.setFieldsValue({
email: this.props.customerData.map((d) => d.email),
});
}
you can convert the logic for the functional component.
In react, is it possible to manage the state of multiple checkboxes with id attribute or other attributes such as data-*?
For the moment all I'm using is the name attribute, however in my project I need to use the id or preferably data-* due to the complexity of the project, but it seems that in react it's isn't possible.
Or am I not understanding in?
import React, { useState } from 'react';
import someData from './someData'
function Checkbox({ name, id, label, checked, onChange }) {
return (
<span>
<input
type="checkbox"
name={name}
id={id}
checked={checked}
onChange={onChange}
/>
<span>{label}</span>
</span>
);
}
function App() {
const [isChecked, setIsChecked] = useState()
const onCheckboxChange = event => {
const target = event.currentTarget;
const name = target.name
const id = target.id;
const checked = target.checked;
setIsChecked({
...isChecked,
[id]: checked // using "id" seems to not work here.
})
}
return (
someData.map(item => {
return (
<Checkbox
name={item.name}
id={item.id}
label={item.name}
onChange={onCheckboxChange}
checked={isChecked}
/>
);
}
);
}
There's no problem to do what you need in React whatsoever.
You may use dataset API to access data-* attributes values to update your state on checkbox change (e.g. if you assign data-chkboxname attribute to your checkbox):
onCheckboxChange = ({target:{checked, dataset:{chkboxname}}}) => {
setIsChecked({
...isChecked,
[chkboxname]: checked
})
}
Following is a quick proof-of-a-concept live-demo:
const { useState } = React,
{ render } = ReactDOM,
rootNode = document.getElementById('root')
const App = () => {
const [isChecked, setIsChecked] = useState({}),
onCheckboxChange = ({target:{checked, dataset:{chkboxname}}}) => {
setIsChecked({
...isChecked,
[chkboxname]: checked
})
},
onFormSubmit = e => {
e.preventDefault()
console.log(isChecked)
}
return (
<form onSubmit={onFormSubmit}>
{
['chckbox1', 'chckbox2', 'chckbox3'].map(checkbox => (
<label key={checkbox}>
{checkbox}
<input
type="checkbox"
data-chkboxname={checkbox}
onChange={onCheckboxChange}
checked={isChecked[checkbox]}
/>
</label>
))
}
<input type="submit" value="submit" />
</form>
)
}
render (
<App />,
rootNode
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>
Try like this
return someData.map((item) => {
return (
<Checkbox
name={item.name}
id={item.id}
label={item.name}
checked={item.checked}
/>
);
});
See codesandbox here
I am trying to pass the state value of a child component up to its parent by using React's useImperativeHandle. However, it appears that my parent component is not receiving the updated state value of the child component when it console logs the child's component value; console.log(componentRef.current.state) always is logged as false.
Why is this not working and how can I accurately receive the mutated state value of my child component in my parent component by passing the necessary ref? Thanks!
index.tsx:
import React from "react";
import ReactDOM from "react-dom";
const Component = React.forwardRef((props, ref) => {
const [state, set] = React.useState(false);
React.useImperativeHandle(ref, () => ({
state
}));
const handleClick = () => {
set(prevState => !prevState);
};
return (
<>
<button type="button" onClick={handleClick}>
Click Me
</button>
<h1>{state ? "On" : "Off"}</h1>
</>
);
});
const App = () => {
const componentRef = React.useRef(null);
React.useEffect(() => {
console.log(componentRef.current.state);
}, [componentRef]);
return <Component ref={componentRef} />;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
If you just want that functionality you can use something like:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
const Component = props => {
const [state, set] = React.useState(false);
useEffect(() => props.callback(state), [state])
const handleClick = () => {
set(prevState => !prevState);
};
return (
<>
<button type="button" onClick={handleClick}>
Click Me
</button>
<h1 >{state ? "On" : "Off"}</h1>
</>
);
};
const App = () => {
return <Component callback={val => console.log(val)} />;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
But if you really need to use ref, just comment it so I'll remove this answer.
I have an object that is outputted from the reactIcons npm package https://www.npmjs.com/package/react-icons I am importing everything from one of the folders with import * as ReactIcons from 'react-icons/fa' I am using sanity studio, I created a text input that takes the value searched and runs a search function that runs a includes on the values from the object I grabbed from the reactIcons fa folder and log's the vales that match. Now I want to take those values and update a react component with useState() Howerver I get the following error Uncaught ReferenceError: setIcon is not defined and SetIcon is the setter function from useState array. Here is my code so far
import React, { useState } from 'react';
import PropTypes from 'prop-types'
import FormField from 'part:#sanity/components/formfields/default'
import PatchEvent, {set, unset} from 'part:#sanity/form-builder/patch-event'
import * as ReactIcons from 'react-icons/fa'
console.log(ReactIcons);
const createPatchFrom = value => PatchEvent.from(value === '' ? unset() : set(String(value)))
const Example = value => {
const [icon, setIcon] = useState();
return (
<div>{icon}</div>
);
}
const search = value => {
console.log(value);
Object.keys(ReactIcons).map(go => {
if (go.toLowerCase().includes(value) === true){
console.log(go);
setIcon(go);
}
})
}
class IconPickerCustom extends React.Component{
static propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired
};
render = () =>{
const {type, value, onChange} = this.props
return (
<>
<FormField label={type.title} description={type.description}>
<input
type="text"
value={value === undefined ? '' : value}
onChange={event => onChange(createPatchFrom(event.target.value))}
ref={element => this._inputElement = element}
/>
</FormField>
<input
type="text"
onChange={event => search(event.target.value)}
/>
{Object.values(ReactIcons).map(X =>{
return (
<>
<X/>
</>
);
})}
{console.log(ReactIcons.Fa500Px)}
<ReactIcons.Fa500Px/>
<Example/>
</>
)
}
}
export default IconPickerCustom;
React has two types of components, class components and function components (formerly stateless components). Hooks are used in function components when you realize you need state inside a function component and prefer not to convert it to a class component.
The useState() Hook allows us to add state in a function component.
Class Component
//initialize state
constructor(props) {
super(props);
this.state = {foo: bar};
}
//set state
this.setState({foo: baz});
//read state
this.state.foo;
Function Component
(with useState() Hook)
//initialize state
const [icon, setIcon] = useState("myInitialValue");
//set state
setIcon("myNewValue");
//read state
{icon}
That being said, you already have a class component so no need to use hooks here.
class IconPickerCustom extends React.Component{
constructor(props) {
super(props);
this.state = { icon: "nothing" };
}
static propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired
};
const createPatchFrom = value => PatchEvent.from(value === '' ? unset() : set(String(value)));
const search = value => {
Object.keys(ReactIcons).map(go =>
({ go.toLowerCase().includes(value) ?
this.setState({icon:go}) : null;})
}
render = () =>{
const {type, value, onChange} = this.props
return (
<>
<FormField label={type.title} description={type.description}>
<input
type="text"
value={value === undefined ? '' : value}
onChange={event => onChange(createPatchFrom(event.target.value))}
ref={element => this._inputElement = element}
/>
</FormField>
<input
type="text"
onChange={event => search(event.target.value)}
/>
{Object.values(ReactIcons).map(X =>{
return (
<>
<X/>
</>
);
})}
{console.log(ReactIcons.Fa500Px)}
<ReactIcons.Fa500Px/>
<Example/>
</>
)
}
}
export default IconPickerCustom;
You are going to want something along those lines. Let me know if you have any questions.
Edit:
I have made it work by getting the target.value from the list, passing that to firstComp.js. Where the onChange method is below, but is this the best practice?
onchange = (e) =>{
let name= e.target.value
let currentName= this.state.foodData.map((item)=>{
if(name==item.name){
console.log(item)
}
})
}
Im doing stephen griders course on react, and trying to implement what he teaches on a personal project, but I cant wrap my head around it.
I have a list, that is looping through an array of objects. When I pick something from that list, I want it to update the state with that object.
The Layout..
DropDownList.js = return a drop down list with all the ingredient names in foodData
DropDownItem = loops through the foodData, returning an option value for each one.
foodData.js = db that looks something like this..
let foodData=[
{"name":"MCT Oil", "brand":"Keppi Keto", "servings":1}
{"name":"Chunky Peanut Butter", "brand":"Skippy"}
]
firstComp.js
import React, { Component} from 'react'
import ReactDOM from 'react-dom'
import foodData from './food/foodData.js'
import DropDownList from './DropDownList.js'
class Layout extends Component {
constructor () {
super()
this.state = {
foodData:foodData,
selectedFood:''
}
this.onchange =this.onchange.bind(this)
}
onchange = (e) =>{
console.log(e.target.value)
}
render () {
return (<div className='home'>
<DropDownList foodData={this.state.foodData} onchange={this.onchange} />
</div>)
}
}
const app = document.getElementById('app')
ReactDOM.render(<Layout />, app)
DropDownList.js
import React from 'react'
import DropDownItem from './DropDownItem.js'
const DropDownList = (props) =>{
****let textInput = React.createRef();**** //REFS
const ingredientItems = props.foodData.map((ingredient, i)=>{
return<DropDownItem key={i} ingredient={ingredient} **ref={textInput}**//REFS />
})
return(
<select value={ingredientItems.name} onChange ={ (e) => props.onchange(e)} >
{ingredientItems}
</select>
)
}
export default DropDownList;
DropDownItem.js
import React from 'react'
const DropDownItem = ({ingredient}) =>{
const itemName = ingredient.name
return(<option value={itemName}>{itemName}</option> )
}
export default DropDownItem;
You don't need to use refs here. You can use some kind of handleChange function in your DropdownList component and send back the value to parent component.
const DropDownList = (props) => {
const handleChange = e => props.onchange( e.target.value );
const ingredientItems = props.foodData.map((ingredient) =>
<DropDownItem key={ingredient.name} ingredient={ingredient} /> );
return (
<select onChange={handleChange}>
{ingredientItems}
</select>
)
}
and in the parent component:
class Layout extends Component {
state = {
foodData,
selectedFood: foodData[0]
};
onchange = name =>
this.setState({
selectedFood: foodData.filter(food => food.name === name)[0]
});
render() {
return (<div className='home'>
<DropDownList foodData={this.state.foodData} onchange={this.onchange} />
</div>)
}
}
or without struggling with values and filter, we can use e.target.options.selectedIndex as OP finds out him/herself :) This is a cleaner way and works as this (only related parts) :
DropdownList.js
const handleChange = e => props.onchange( e.target.options.selectedIndex);
Layout.js
onchange = index => this.setState({ selectedFood: foodData[index] });