How to use a function on onClick instead of onChange? - reactjs

i have an search function for searching in the table. Now. I want to use the search function on the onClick of the icon instead of the onChange of the input field. I don't think i need the throttle for that. I try to use the function setGlobalFilter directly inside the handleClick but it won't work
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = React.useState(globalFilter)
const onChange = React.useCallback(
value => {
const throttledSetGlobalFilter = throttle(
value => {
setGlobalFilter(value || undefined)
},
2000
)
throttledSetGlobalFilter(value)
},
[setGlobalFilter]
)
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value || ''}
onChange={(e) => {
setValue(e.target.value)
onChange(e.target.value)
}}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
)
function handleClick() {
}
}

If I understand your question, you want the icon click to invoke the setGlobalFilter callback with the input value. Remove the onChange handler and directly update value state. Call setGlobalFilter with the state value when the icon is clicked.
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = useState(globalFilter ?? '');
const handleClick = () => setGlobalFilter(value);
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
);
}
If you need to throttle the setGlobalFilter then the following may help.
function GlobalFilter({ globalFilter, setGlobalFilter }) {
const [value, setValue] = useState(globalFilter ?? '');
const handleClick = throttle(
() => setGlobalFilter(value),
2000,
);
return (
<span className={styles.componentGlobalFilter}>
<input
className={styles.input}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder={`Zoek in deze tabel`}
/>
<Icon onClick={handleClick} layoutClassName={styles.icon} {...{ icon }} />
</span>
);
}

Related

Why my checkbox doesn't work in my Dialog?

I create a component for my Dialog and my Checkbox my issue is when my checkbox is not in the Dialog the update works but when it's inside it doesn't work. I don't understand why.
const Popup = ({ title, handleClose, openned, children }) => {
return (
<Dialog className='react-popup-template' fullWidth={true} maxWidth='sm' open={openned} onClose={handleClose} aria-labelledby="parent-modal-title" aria-describedby="parent-modal-description">
<DialogContent id="modal-description" >
<div>
{title && <div><h4 style={{ textAlign: 'center', fontWeight: 'bold', fontSize : '23px' }}>{title}</h4><br/></div>}
{children}
</div>
</DialogContent>
</Dialog>
);
}
const CheckBox = (value, onChange) => {
return (
<label>
<input type='checkbox' value={value} onChange={onChange} />
</label>)
}
const App = () =>{
const [openPopup, setOpenPopup] = React.useState(false)
const [checked, setChecked] = React.useState(false)
const [title, setTitle] = React.useState('')
const [description, setDescription] = React.useState('')
const showModal = (title) =>{
setTitle(title)
setDescription(<CheckBox value={checked} onChange={() => {setChecked(!checked)}} />)
}
return (
<button onClick={() => {showModal('Title')}}>showModal</button>
<PopupTemplate title={title} handleClose={() => { setOpenPopup(false) }} openned={openPopup}>
{description}
</PopupTemplate>)
}
In your Checkbox you should either destructure your props
const CheckBox = ({ value, onChange }) => {
return (
<label>
<input type="checkbox" value={value} onChange={onChange} />
</label>
);
};
Or use your props via the props value
const CheckBox = (props) => {
return (
<label>
<input type="checkbox" value={props.value} onChange={props.onChange} />
</label>
);
};
EDIT:
The state only updates the first time you click the checkbox. Using the callback in the setChecked method will solve this.
...
setDescription(
<CheckBox
value={checked}
onChange={() => {
setChecked((prevChecked) => !prevChecked);
}}
/>
);
...
PS: I don't now if its just a copy/paste error, but you're missing setOpenPopup(true) in your showModal function.
Try this, as mui uses forwarRef for its components this should work,
setDescription(<CheckBox checked={checked} onChange={e => setChecked(!checked)} />)

onBluer is activated before the button's onClick to clear the input - ReactJS

Before the button to clear the input is called the onBlur event is activated and my component is disassembled before clearing the text.
export default function Search(): JSX.Element {
const [search, setSearch] = useState('');
const [isFocus, setIsFocus] = useState(false);
const handleSearch = useCallback(e => {
setSearch(e.target.value);
}, []);
return (
<>
<InputContainer>
<IconSearch>
<FiSearch color={isFocus ? backgroundOrange : '#A0ACB2'} />
</IconSearch>
<SearchInput
placeholder="Qual cidade vocĂȘ procura?"
onChange={handleSearch}
value={search}
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
/>
{isFocus && (
<ButtonClose onClick={() => setSearch('')}>Close</ButtonClose>
)}
</InputContainer>
</>
);
}

Why is my check complete button is not working

I wanna make the function that whenever i click on the complete button, the complete state will turn true and there will be a line through in my todo. However, my function is not working and i don't know why? can anyone help me? Thank you so much!
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now() }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 complete ? "line-through" : "">{todo.value}</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={()=>setComplete(!complete)}>complete</button>
</div>
))}
</div>
);
}
export default App;
Sandbox link: https://codesandbox.io/s/stoic-mendeleev-6fetl?file=/src/App.js
complete state missing
h3 add style={{ textDecoration: complete ? "line-through" : "" }}
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
const [complete, setComplete] = useState(false);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now() }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 style={{ textDecoration: complete ? "line-through" : "" }}>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={() => setComplete((perv) => !perv)}>complete</button>
</div>
))}
</div>
);
}
export default App;
Try this
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
const [complete, setComplete] = useState({});
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
const id = Date.now();
setTodos([...todos, { value, id }]);
setComplete({ ...complete, [id]: false });
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
console.log(complete);
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => (
<div key={todo.id}>
<h3 className={complete[todo.id] ? "line-through" : ""}>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button
onClick={() =>
setComplete({ ...complete, [todo.id]: !complete[todo.id] })
}
>
complete
</button>
</div>
))}
</div>
);
}
export default App;
*Note you will need to specify styling for your line-through class to see the strike through
I have updated the codesandbox. you can have look on the codesandbox.
https://codesandbox.io/s/compassionate-frost-8255y
Try below code.
You need to create setComplete menthod.
import React, { useState } from "react";
function App() {
const [value, setValue] = useState("");
const [todos, setTodos] = useState([]);
// you can use the submit itself, no need for an extra addTodo function
const handleSubmit = (e) => {
e.preventDefault();
setTodos([...todos, { value, id: Date.now(), complete: false }]);
setValue("");
};
const handleDelete = (id) => {
setTodos((todos) => todos.filter((todo) => todo.id !== id));
};
const handleComplete = (id) => {
let compTodo = todos.filter((todo) => todo.id === id);
compTodo[0].complete = true;
let allTodo = todos.filter((todo) => todo.id !== id);
setTodos([...allTodo, compTodo[0]]);
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
onChange={(e) => setValue(e.target.value)}
type="text"
placeholder="add todo"
value={value}
/>
<button type="submit">Add</button>
</form>
{todos.map((todo) => {
return (
<div key={todo.id}>
<h3
style={{
textDecoration: todo.complete ? "line-through" : "none"
}}
>
{todo.value}
</h3>
<button onClick={() => handleDelete(todo.id)}>X</button>
<button onClick={() => handleComplete(todo.id)}>complete</button>
</div>
);
})}
</div>
);
}
export default App;

useState not updating data when passing from parent function component using ref

I am trying to send data to child function component where I am binding form fields with that data. It works fine on first call, but when I am calling 2nd time the data never update in state, its always shows the first one.
This is parent which use the ref of child component
export default function Form1() {
const [count, setCount] = useState(0);
const [counter, setCounter] = useState(10);
const AddNewRef = useRef();
const clickMe=() => {
setCount(count+1);
setCounter(counter*2);
AddNewRef.current.showDrawer(counter*2);
}
return (
<div>
<p>You clicked count: {count} & counter: {counter} times</p>
{
count > 10 ?
(
<p className='red'>your count is greater then 10</p>
) :
(
<p className='green'>your count is less then 10</p>
)
}
<button onClick={() => clickMe()}>
Click me
</button>
<AddNew ref={AddNewRef} Count={count} Counter={counter} />
</div>
)
}
This is child component
const AddNew=forwardRef((props, ref) => {
const[objCounter, setobjCounter] = useState(null);
useImperativeHandle(
ref,
() => ({
showDrawer(count) {
setobjCounter(count);
//only shows at first click at parent, Not updating on 2nd, 3rd click from parent and so on....
}
}),
)
return (
<>
<Drawer
title={<span> <i className='fa-solid fa-kaaba' /> Haj Setup Form</span>}
width={window.innerWidth > 900 ? 800 : window.innerWidth - 50}
onClose={onClose}
visible={visible}
bodyStyle={{ paddingBottom: 80 }}
extra={
<Space>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose} type="primary">
Submit
</Button>
</Space>
}
>
<Form
style={{display: formVisible ? 'block' : 'none'}}
form={form}
layout="vertical"
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
hideRequiredMark>
<Row gutter={16}>
<Col xs={24} sm={24} md={24} lg={24}>
<Form.Item
name="packageName"
label="Package Name"
rules={[{ required: true, message: 'Please enter package name' }]}
initialValue={objCounter}
>
<Input style={{width: '100%'}}
maxLength={100} />
</Form.Item>
</Col>
</Row>
</Form>
</Drawer>
</>
)
});
export default AddNew
Since the state updates are working and you are simply wanting to update the form field, you can use the returned form reference from the useForm hook to update the form state. In this case, update the packageName field.
const AddNew = forwardRef((props, ref) => {
const [objCounter, setobjCounter] = useState(13);
const [visible, setVisible] = useState(false);
const [formVisible, setformVisible] = useState(true);
const [form] = Form.useForm();
useImperativeHandle(ref, () => ({
showDrawer(count) {
setobjCounter(count);
setVisible(true);
form.setFieldsValue({
packageName: count // <-- update the specific field
});
}
}));
const onClose = () => {
setVisible(false);
};
return (
...
);
});

How do I implement a custom handleChange function on Formik?

In an input element, handleChange function would receive the event object from the onChange event. How do I create a custom handleChange function for non-input fields like the following?
import React from 'react';
import { useFormik } from "formik";
const SomeForm = () =>
{
const { handleChange, handleSubmit, values } = useFormik({
initialValues: {
type: `company`, name: ``,
},
onSubmit: values => {
console.log(JSON.stringify(values, null, 2));
},
});
return (
<div>
<form onSubmit={ handleSubmit }>
<label>Type</label>
<ul>
<li className={ values.type === `company` && `active` }
onClick={() => handleChange(/* some custom handle change */)} >
Company
</li>
<li className={ values.type === `individual` && `active` }
onClick={() => handleChange(/* some custom handle change */)} >
Individual
</li>
</ul>
<label>Full Name</label>
<input type="text"
name="name"
value={ value.name }
onChange={ handleChange } />
</form>
</div>
)
};
export default SomeForm;
use setField('fieldName',value) method of form object provided in render props pattern of Field component.
I think this is what you're after. You can add your custom code after field.onChange(e).
// Custom field
const MyTextField = ({ label, ...props }) => {
const [field, meta] = useField(props);
return (
<>
<input {...field} {...props}
onChange={e => {
// The original handler
field.onChange(e)
// Your custom code
console.log('I can do something else here.')
}}
className={ meta.error && 'is-invalid'}` } />
{meta.touched && meta.error && (
<div>{meta.error}</div>
)}
</>
);
};
And use it like so
<MyTextField name="entry" type="text" />

Resources