Unable to use react form data in useEffect hook - reactjs

I have an antd form where I am able to get the form data in onFinish function upon hitting submit button in which I wanted to use uesEffect hook and dispatch an action with form data as payload to redux saga but I got following error
React Hook "useEffect" is called in function "onFinish" that is neither a React function component nor a custom React Hook function.
If I write useEffect hook outside the onFinsh function, I am unable to get the form data/values
Please suggest a workaround to get the form data values outside of onFinish function
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Checkbox } from 'antd';
const Demo = () => {
const onFinish = (values) => {
// alert(JSON.stringify(values['username']));
useEffect(() => {
// dispatch an action with values as payload
}, []);
};
console.log(values) // UNABLE TO GET VALUES HERE...HOW TO GET IT???
return (
<Form
name="basic"
onFinish={onFinish}>
<Form.Item
label="Username"
name="username">
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));

It looks like you don't even need the useEffect() hook. Just dispatch the action from within the onFinish() and have state store the values
const Demo = () => {
const [ values, setValues ] = useState([]);
const onFinish = (recievedValues) => {
// dispatch here
setValues(recievedValues);
}
console.log(values) // <-- you can get it here
return (<div> ... </div>);
};
Or better yet, since you are already saving the values in redux during dispatch, you should use that in your render code as well:
import { useSelector } from 'react-redux';
const Demo = () => {
//point to the state where your data is
const stateValues = useSelector(state => state.your.data);
const onFinish = (recievedValues) => {
// dispatch here
}
console.log(stateValues) // <-- you can get it here
return (<div> ... </div>);
};

useEffect can only be called at the top level of your component, not within a function. In this case, you shouldn't need useEffect to dispatch the action, and instead can just do so directly inside onFinish.

Related

Cannot update a component (`TodoForm`) while rendering a different component (`TodoTask`). [SOLUTION] [React Redux To-Do App]

WHILE WRITING THIS POST I REALIZED WHAT THE SOLUTION WAS
Every time I dispatch a task to my store the following error occurs:
I have some idea of why it happens. It happens precisely when I try to get the to-do list using useSelector and then mapping through the list. However, the mapping is not the issue but rather returning a react component on the map function. It works just fine if I do not return a functional component and instead use HTML. So the issue, from my POV, is returning a react functional component while passing props to it on a map function.
Here's the code for my home component:
import Input from '../components/Input';
import TodoForm from '../components/TodoForm';
function Home() {
document.title = "MyTodo | Home"
return (
<div className="App">
<h1>MyTodo</h1>
<Input />
<TodoForm />
</div>
);
}
export default Home;
The input component where the action is being dispatched on key down:
import {useState} from 'react'
import { useDispatch } from 'react-redux';
import { todoActions } from '../store/todo';
const Input = () => {
const [inputText, setInputText] = useState("");
const dispatch = useDispatch();
const handleChange = (e) => setInputText(e.target.value)
const handleKeyPress = (event) => {
if (event.code === "Enter") {
// if the expression is false, that means the string has a length of 0 after stripping white spaces
const onlyWhiteSpaces = !inputText.replace(/\s/g, "").length;
!onlyWhiteSpaces &&
dispatch(
todoActions.addTask({ label: inputText, done: false })
);
setInputText("");
}
};
return (
<input
type="text"
onKeyDown={(e) => handleKeyPress(e)}
onChange={(e) => handleChange(e)}
value={inputText}
/>
);
}
export default Input
The TodoForm where I am using useSelector to get the todo list from the redux store and mapping thru it:
import { useSelector } from "react-redux";
import { v4 as uuidv4 } from "uuid";
import TodoTask from "./TodoTask";
const TodoForm = () => {
const tasks = useSelector((state) => state.todo.taskList);
const renderedListItems = tasks.map((task, index) => {
return (
<TodoTask
key={uuidv4()}
task={task}
targetIndex={index}
/>
);
});
return <div className="container">{renderedListItems}</div>;
};
export default TodoForm;
Finally the TodoTask component which is the child component being returned on the map function above:
import { useDispatch } from "react-redux";
import { todoActions } from "../store/todo";
const TodoTask = ({ task, targetIndex }) => {
const {text, done} = task;
console.log("Task: ", task);
const dispatch = useDispatch()
const removeTask = dispatch(todoActions.deleteTask(targetIndex))
return (
<div
className="alert alert-primary d-flex justify-content-between"
role="alert"
>
{text}
<button type="button" className="btn-close" onClick={()=>removeTask}></button>
</div>
);
};
export default TodoTask;
This is my first time facing this issue, and I know it has something to do with redux and how the useSelector hook forces a component to re-render. So the useSelector is re-rendering the TodoForm component, and since we are mapping and returning another component, that component is also being rendered simultaneously. At least, that is how I understand it. Let me know if I am wrong.
Things I have tried:
Wrapping the TodoTask in React.memo. Saw it somewhere as a possible solution to this kind of issue, but that did not work.
Passing shallowEqual as a second parameter on the TodoForm useSelector. This does prevent the page from going into an infinity loop, but the tasks show up empty but are being added to the redux store. However, with this method, the first warning stills shows up, and the console log in the TodoTask component does not execute.
Passing shallowEqual as a second parameter on the TodoForm useSelector. This does prevent the page from going into an infinity loop but the tasks show up empty but are being added to the redux store. However, with this method, the first warning stills shows up and the console log in the TodoTask component does not execute.
I realized what I was doing wrong while writing this part. The console log in the TodoTask component was working, but I had the browser console filtering for errors only. When I check the messages section, I saw everything working fine. Then when I checked the Task component, I noticed I was trying to read a property that did not exist and hence why the tasks had no text.
In other words, the solution was adding shallowEqual as second parameter of the useSelector hook in my TodoForm component that was the one mapping thru the todo tasks array. As I said, useSelector forces a component to re-render. shallowEquals checks if the existing state isn't the same as we already had and avoids unnecessary re-renders, which can lead my application to exceed the maximum update length.
Code fix [Solution]:
import { memo } from "react";
import { shallowEqual, useSelector } from "react-redux";
import { v4 as uuidv4 } from "uuid";
import TodoTask from "./TodoTask";
const TodoForm = () => {
// shallowEqual prevents unnecessary re-renders which can lead to an infinite loop
// it compares the current state with the previous one, if they are the same, it does not re-render the component
const tasks = useSelector((state) => state.todo.taskList, shallowEqual);
const renderedListItems = tasks.map((task, index) => {
return (
<TodoTask
key={uuidv4()}
task={task}
targetIndex={index}
/>
);
});
return <div className="container">{renderedListItems}</div>;
};
export default memo(TodoForm);
Honestly, I have been stuck on this since yesterday and I cannot believe I realize the solution just when I was about to ask for help. Hope this helps anyone else who faces a similar issue in the future.

Dispatch clear all my data form input reactjs

I use reactjs. But when I handle onClick event. After I clicked on Button and dispatch an event. It's clear all my data in Input. I don't know why the data is cleared and how can prevent it.
import React, { useEffect } from "react";
import { Input, Form, Checkbox, Button, Row, Col, notification } from "antd";
import { loginRequest } from "../../../store/userStore";
import { useDispatch, useSelector } from "react-redux";
export default function SignInPage() {
const dispatch = useDispatch()
const handleSubmit = (e) => {
dispatch(loginRequest());
}
return (
<>
<Input>
</Input>
<Input>
</Input>
<Button onClick={() => {
handleSubmit()
}}>Click</Button>
</>
)
}
Issue
I'm guessing these inputs and button are being rendered with a form element and you aren't preventing the default form action from occurring. Button elements have a type="submit" by default, so when clicked will invoke the default form action and reload the page (clearing the inputs).
Solution
Call preventDefault on the event object.
const handleSubmit = (e) => {
e.preventDefault(); // <-- prevent the default
dispatch(loginRequest());
}

how to set value in hooks

I have a problem with hooks in ReactJS
as you see here i defined a prop that should call from child component
but when i want to change the value by calling change component it doesn't work and my state doesn't set.
can someone help me?
don't forget to read the comments
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
Use setState with a callback function
const setSelectedCollection = (e) => {
setCollection((state)=> {...state, e});
}
setCollection(e) - wont update the state immediately.
I want to Understand SetState and Prevstate in ReactJS
This might help you around, the useEffect will be called on each colletion update
import React, { useState, useEffect } from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
useEffect(() => {
console.log(collection)
}, [collection])
return (
<div>
<Collection onChoosed={(e) => setCollection(e)} />
</div>
)
}
export default SingleReminderPage;
it seems like the setCollection is called after the logging action to check something like that you can print the collection value on the component itself
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
{collection}
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;

Passing String value through Props returned from useSelector Hook

I am working on ReactJS modal dialog and bind the values from redux slice through the useSelector hook. Currently I have two functions which are already dispatching using useDispatch hook and setting the props with 2 functions(onCancelHandler, submitHandler). Here I need to keep one more field which is string value(userName) and tried to keep that and usig the string value approvedUser in DeleteUserModalContent through the props. Initially I am able to get the value from props in DeleteUserModalContent
component but when submitHandler is executed the following error is occured.
Can't read property 'userName' which is undefined
Error at this line:
const approvedUser: string = selectedUser.userName;
Can any one tell me what is wrong here?
Thanks in Advance
Code Snippet:
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Modal } from '#material-ui/core';
import { AppState } from 'store/rootReducer';
import { hideModal } from 'store/common/modalSlice';
import { submitAction } from 'store/user-actions';
import { DeleteUserModalContent } from './DeleteUserModalContent';
export const DeleteUserModal: React.FC<{}> = () => {
const dispatch = useDispatch();
const selectedUser = useSelector((state: AppState) => {
const selectedUserId =
state.selectUserSlice.selectedUsers[0];
return state.userState[selectedUserId];
});
const onCancelHandler = () => {
dispatch(hideModal());
};
const submitHandler = () => {
dispatch(
submitAction(selectedUser.userName)
);
};
const approvedUser: string = selectedUser.userName;
console.log(selectedUser.userName);
const props = {
onResetHandler,
submitHandler,
approvedUser
};
return (
<Modal>
<>
<DeleteUserModalContent {...props} />
</>
</Modal>
);
};
When we use the returned value from the useSelector hook and use the same in other component DeleteUserModalContent by setting into props. Here we are able to use the approvedUser value initially but when submitHandler function is dispatched selectedUser.userName value becomes undefined, So we can put the condition check below:
const approvedUser: string = selectedUser?.userName
to avoid the above mentioned error.

Using redux's useDispatch in a custom hook yields an error

I'm trying to implement useDispatch in a custom hook that dispatches a redux action, but I'm getting the following error:
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
CODE:
modules file
import { useDispatch, useSelector } from 'react-redux'
export function useFetchEvents() {
const dispatch = useDispatch()
const { items, loading } = useSelector(state => state.events)
if (items.length === 0) {
dispatch(requestEvents(true))
}
}
functional component
import { useFetchEvents } from '../../../modules/events'
const FrontPage = () => {
return(
<div>
Front Page
<button onClick={useFetchEvents}>
Fetch events
</button>
</div>
)
}
export default FrontPage
I've seen the error and read the rules regarding hooks, but if I understand it correctly I should be able to use useDispatch in a custom hook. Like in the following working examples:
https://github.com/mikeour/drinks_drinks_drinks/blob/master/src/hooks/index.js
Then number of hook calls in each invocation should be the same (that's why you are not allowed to call hooks inside if statements).
To achieve this useFetchEvents hook should return a function that can be conditionally called, e.g. onClick
change useFetchEvents like so:
export function useFetchEvents() {
const dispatch = useDispatch()
const { items, loading } = useSelector(state => state.events)
return () => {
if (items.length === 0) {
// Redux action. requestEvents returns object with type.
dispatch(requestEvents(true))
}
}
}
Then in your component do this:
const FrontPage = () => {
const fetchEvents = useFetchEvents()
return(
<div>
Front Page
<button onClick={() => fetchEvents()}>
Fetch events
</button>
</div>
)
}

Resources