I'm trying to set up a simple react-redux flow where an input updates state and a form submits the value in the component's state to a redux action function. However, whenever the form submits, the page reloads and when I add e.preventDefault() to the submit function, I get
TypeError: e.preventDefault is not a function
I've tried adding e.preventDefault() to the submitToRedux function but when I add do, I get TypeError: e.preventDefault is not a function
Here is my Child1.js:
import React, { useState } from "react";
import { changeName } from "../redux/name/name.actions";
import { connect } from "react-redux";
function Child1(state) {
const [name, setName] = useState("");
const changeHandler = e => {
e.preventDefault();
setName(e.target.value);
};
const submitToRedux = e => {
// e.preventDefault();
changeName(name);
};
return (
<div>
<h2>CHILD ONE</h2>
<form onSubmit={submitToRedux(name)}>
<input type="text" onChange={changeHandler} />
<button type="submit">SUBMIT</button>
<h2>name in Child1 state: {name}</h2>
<h2>name in redux: {state.name.name}</h2>
</form>
</div>
);
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps)(Child1);
App.js:
import React from "react";
import Child1 from "./components/Child1";
function App() {
return (
<div className="App">
<Child1 />
</div>
);
}
export default App;
root-reducer.js:
import { combineReducers } from "redux";
import nameReducer from "./name/nameReducer";
export default combineReducers({
name: nameReducer
});
and nameReducer.js:
import NameActionTypes from "./name.types";
const INITIAL_STATE = {
name: "Mike"
};
const nameReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case NameActionTypes.CHANGE_NAME:
return {
...state,
name: action.payload
};
default:
return state;
}
};
export default nameReducer;
I expect it to update the state.name.name value in Child1.js to whatever is submitted from the state in the Child1.js component when the form is submitted but instead it just reloads the page and since I'm not persisting it to local storage it just remains blank. When I add e.preventDefault() I expect it to stop reloading the page when the form submits but it then says that
e.preventDefault is not a function
It's because you are not passing the submit event to submitToRedux function.
You should pass it to your function like this:
<form onSubmit={(e) => submitToRedux(e, name)}>
and then you handle it in you function like this:
const submitToRedux = (e, name) => {
e.preventDefault();
changeName(name);
};
Here is how child1.js will be:
import React, { useState } from "react";
import { changeName } from "../redux/name/name.actions";
import { connect } from "react-redux";
function Child1(state) {
const [name, setName] = useState("");
const changeHandler = e => {
e.preventDefault();
setName(e.target.value);
};
const submitToRedux = (e, name) => {
e.preventDefault();
changeName(name);
};
return (
<div>
<h2>CHILD ONE</h2>
<form onSubmit={(e) => submitToRedux(e, name)}>
<input type="text" onChange={changeHandler} />
<button type="submit">SUBMIT</button>
<h2>name in Child1 state: {name}</h2>
<h2>name in redux: {state.name.name}</h2>
</form>
</div>
);
}
const mapStateToProps = state => ({
name: state.name
});
export default connect(mapStateToProps)(Child1);
Multiple issue's with your code,
First, you are writing state as argument to Child1 component
function Child1(state) {
which should be,
function Child1(props) {
You should set this props to your state,
const [name, setName] = useState(props.name);
Your input should be controlled,
<input type="text" onChange={changeHandler} value={name} />
You should print name like this,
<h2>name in Child1 state: {name}</h2>
<h2>name in redux: {props.name}</h2>
Your form submit method should be like this,
<form onSubmit={submitToRedux}>
And finally your submitToRedux function,
const submitToRedux = e => {
e.preventDefault(); //Now this will work
changeName(name); //As we have controlled input, we direclty take name from state
};
You just need to pass the function that will get called once the form is submitted.
<form onSubmit={submitToRedux}>
But instead you are actually calling it right away:
<form onSubmit={submitToRedux(name)}>
When you just pass the function, the form will take care of calling it with a submit event as parameter.
In your code the error says the parameter e should contain a function preventDefault, which clearly is not defined in the variable you are passing in as parameter when you do: submitToRedux(name)
Related
On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};
Hi I am new to React and I just can't wrap my head around some basic things.
One of these things is the following.
Why does the state of itemData change when calling the function with onChange on the textarea element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form>
<textarea
onChange={handleitemData}
placeholder="What you have in mind ..."
></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
But not when calling the function with onSubmit on the form element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
I know the React way is if that does not work try something else but I am trying to actually understand what is going on.
I apreciate your response.
You must prevent the default behavior of the browser on submit:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
//The line below is necessary
e.preventDefault()
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
attached is a sandbox to see it in action https://codesandbox.io/s/infallible-lederberg-3oj1w?file=/src/App.js:0-630
When I submit text in the form, I want to be able to see that text in state via console.log.
I added a console.log to trigger directly after state has been added, but I don't see anything in my console, what am I doing wrong?
What I would like to see: I submit "test1" then I submit "test2", I then want to see in my console "test1, test2" in state.
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useState('');
const addToDoItem = (event) => {
setToDoItem(event.target.value), function(){
console.log(toDoItem)
}
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
You can log the change in states using a useEffect
I'd suggest making a helper function if you tend to do it often:
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
Which you'd use as follows:
useLog('toDoItem', toDoItem);
I set up an example where the toDoItem is logged as it changes and when you submit, it also logs the change in a todoItems array
const { useState, useEffect } = React;
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
function App() {
return (
<div>
<Field />
</div>
);
}
function Field() {
const [toDoItem, setToDoItem] = useState('');
useLog('toDoItem', toDoItem);
const [todos, setTodos] = useState([]);
useLog('todos', todos);
const changeTodo = (event) => {
setToDoItem(event.target.value);
};
const addTodoItem = (event) => {
event.preventDefault();
setTodos((prev) => prev.concat(toDoItem));
setToDoItem('');
};
return (
<form onSubmit={addTodoItem}>
<input type="text" value={toDoItem} onChange={changeTodo} />
<input type="submit" value="Add" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
useState doesn't provide callback after setting value. You can use useEffect instead.
React.useEffect(() => {
console.log(toDoItem);
}, [toDoItem]);
EDIT
It seems like that you want to get toDoItem value on submit. The problem is that the page is reloaded when form is submitted. You can use event.prefentDefault() to stop refreshing on form submission.
<form onSubmit={onSubmit}>
const onSubmit = (event) => {
event.preventDefault()
console.log(toDoItem)
}
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useStateWithCallback('', toDoItem => {
console.log(toDoItem);
});
const addToDoItem = (event) => {
setToDoItem(event.target.value);
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
I am developing a TODO app using react redux typescript. I am unable to dispatch an action. I am new to redux so stuck in it. I am creating an event onclick handleSubmitButton then it is throwing dispatch error. Can someone help me in that ?
AddToDo.tsx
const AddToDo: React.FC = () => {
const [inputValue, setInputValue] = useState<string>("");
const handleSubmitButton = (e: React.MouseEvent<HTMLButtonElement>)=> {
e.preventDefault()
console.log(inputValue, "inputValue");
dispatch(addTodo(inputValue))
}
return (
<div>
<form noValidate>
<div className="form-group">
<label htmlFor="todoitem">Todo Item:</label>
<input type="text" className="form-control" placeholder="Enter Text" value={inputValue} id="todoitem" onChange={handleEditTodoItem} />
</div>
<button type="submit" className="btn btn-primary" onClick={handleSubmitButton}>Submit</button>
</form>
</div>
)
}
export default connect()(AddToDo)
Below is my action code -
export const addTodo = (name: string): AddTodoAction => (
{
type: ActionTypes.ADD_TODO,
payload: {
todo: {
id: nextId++,
name: name,
done: false
}
}
}
)
and Now my reducer code -
export function reducer(state: State = initialState, action: Action) {
switch (action.type) {
case ActionTypes.ADD_TODO: {
const todo = action.payload.todo
return {
...state,
todos: [...state.todos, todo]
}
}
}
}
The reason you are getting a "dispatch error" is because dispatch is probably undefined. This is because you failed to:
import { useDispatch } from 'react-redux'
// ...
const dispatch = useDispatch();
If using the hooks to hook into redux, you don't need to "connect" anything, nor write mapDispatchToProps. Just useSelector and useDispatch are enough to hook into the redux store, which makes for a much friendlier experience.
This answer is based on not using Redux hooks.
You are close, but you forgot to import your dispatch in connect() using mapDispatchToProps. This would look like something like this:
import { Dispatch } from 'redux';
import { addToDo } from '../action'; // replace this import with the correct path to your action
type AddToDoProps = {
addToDo: (inputValue: string) => void;
}
const AddToDo: React.FC<AddToDoProps> = ({ addToDo }) => {
const [inputValue, setInputValue] = useState<string>("");
const handleSubmitButton = (e: React.MouseEvent<HTMLButtonElement>)=> {
e.preventDefault()
console.log(inputValue, "inputValue");
addTodo(inputValue)
}
return (
<div>
<form noValidate>
<div className="form-group">
<label htmlFor="todoitem">Todo Item:</label>
<input type="text" className="form-control" placeholder="Enter Text" value={inputValue} id="todoitem" onChange={handleEditTodoItem} />
</div>
<button type="submit" className="btn btn-primary" onClick={handleSubmitButton}>Submit</button>
</form>
</div>
)
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
addToDo: (inputValue: string) => dispatch(addToDo(inputValue))
})
export default connect(null, mapDispatchToProps)(AddToDo)
Note: You are getting your dispatch method as a prop in your component which means you will have to define this prop in Typescript.
type AddToDoProps = {
addToDo: (inputValue: string) => void;
}
Add this type to your component and define your dispatch prop in order to use it:
const AddToDo: React.FC<AddToDoProps> = ({ addToDo }) => ...
I have a component with an input that when submitted is meant to pass the input text to store. I can't figure out how to preventDefault() when I submit the form.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addItem } from '../actions';
const ItemInput = (props) => {
return (
<div>
<form onSubmit={() => props.addItem('test')}>
<input
placeholder="addItem"
/>
</form>
</div>
);
}
const mapStateToProps = (state) => {
return { addItem: state.addItem };
}
export default connect(mapStateToProps, {
addItem: addItem
})(ItemInput);
I know how to do this in react, but it doesn't seem to work the same way, I keep getting an unexpected token error that doesn't make any sense, probably because the syntax just doesn't work the same way with redux and store. This also isn't a button issue, I'm submitting the form after pressing return.
This part of your code is just a function, you can expand it as you want:
<form onSubmit={() => props.addItem('test')}>
So, you can do:
<form onSubmit={e => {
e.preventDefault();
props.addItem('test');
}}>
Or move this handler into a function:
const handleSubmit = e => {
e.preventDefault();
props.addItem('test');
}
// ...
<form onSubmit={handleSubmit}>