React Redux - app/store resets after added item to store - javascript

I cant figure out why when I add an item to my store the app resets. I can see for a split second that its being added to my list but then I can see all my component flicker and reload. Inspecting the console doesn't bring up any warnings.
I also plugged in just my addProduct component and productsSlice into the redux template and it also reloaded the app.
Basic HTML for adding a product with hooks:
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { addProduct } from '../_reducers/productsSlice'
const AddProduct = () => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('')
const [price, setPrice] = useState('')
const onTitleChanged = e => setTitle(e.target.value);
const onDescriptionChanged = e => setDescription(e.target.value);
const onPriceChanged = e => setPrice(e.target.value);
const dispatch = useDispatch();
return (
<section>
<h2>Add a new Product</h2>
<form>
<label htmlFor="Title">Title</label>
<input
type="text"
name="title"
value={title}
onChange={onTitleChanged} />
<label htmlFor="Price">Price</label>
<input
type="text"
value={price}
onChange={onPriceChanged} />
<label htmlFor="Description">Description</label>
<textarea
value={description}
onChange={onDescriptionChanged} />
<button onClick={() => dispatch(addProduct({
title: title,
price: price,
description: description
}))}>
Submit
</button>
</form>
</section>
)
}
export default AddProduct;
My Reducer:
import { createSlice } from '#reduxjs/toolkit'
const initialProductsState = [
// { name: '1', price: 10, description: 'testDescrip1' },
// { name: '2', price: 20, description: 'testDescrip2' },
// { name: '3', price: 30, description: 'testDescrip3' },
{ name: '4', price: 40, description: 'testDescrip4' }]
export const productsSlice = createSlice({
name: 'products',
initialState: initialProductsState,
reducers: {
addProduct: (state, action) => {
state.push(action.payload)
},
}
})
export const { addProduct } = productsSlice.actions;
export default productsSlice.reducer
Listing of the products:
import React from 'react';
import { useSelector } from 'react-redux'
import { Row, Col } from 'react-bootstrap'
import ProductItem from "./ProductItem"
const ProductList = () => {
const products = useSelector(state => state.products)
let numberRendered = 3;
const renderedProducts = products.map(item => (
<ProductItem product={item} />
))
return (
<div id="ProductList">
<Row>
<Col>
{renderedProducts}
</Col>
</Row>
</div>
)
}
export default ProductList;
And the product component styled with react-bootstrap:
import React from 'react';
import {productSlice} from '../_reducers/productsSlice';
import { Card, Col, Row } from 'react-bootstrap'
const ProductItem = (props) => {
return (
<Card>
<Card.Body>
<Row>
<Col md={4}>
<Card.Img variant="float" src={process.env.PUBLIC_URL + 'placeholder.png'} />
</Col>
<Col md={8}>
<Card.Title>{props.product.title}</Card.Title>
<Card.Text>{props.product.description}</Card.Text>
<Card.Subtitle>{props.product.price}</Card.Subtitle>
</Col>
</Row>
</Card.Body>
</Card>
)
}
export default ProductItem;
store:
import { configureStore } from '#reduxjs/toolkit'
import productsSlice from '../src/_reducers/productsSlice'
const store = configureStore({
reducer: {
products: productsSlice,
}
})
export default store;

Pretty sure you just need to add an event.preventDefault() to the form's submit event
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { addProduct } from '../_reducers/productsSlice'
const AddProduct = () => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('')
const [price, setPrice] = useState('')
const onTitleChanged = e => setTitle(e.target.value);
const onDescriptionChanged = e => setDescription(e.target.value);
const onPriceChanged = e => setPrice(e.target.value);
const dispatch = useDispatch();
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addProduct({
title: title,
price: price,
description: description
}))
}
return (
<section>
<h2>Add a new Product</h2>
<form onSubmit={handleSubmit}>
<label htmlFor="Title">Title</label>
<input
type="text"
name="title"
value={title}
onChange={onTitleChanged} />
<label htmlFor="Price">Price</label>
<input
type="text"
value={price}
onChange={onPriceChanged} />
<label htmlFor="Description">Description</label>
<textarea
value={description}
onChange={onDescriptionChanged} />
<button type="submit">
Submit
</button>
</form>
</section>
)
}
export default AddProduct;
Without the preventDefault submitting the form will submit a get request to the same route, which will cause the component to reload.

Related

REACT Why do I get the error "Uncaught TypeError: createTask is not a function" when calling a function passed as a parameter?

I am getting this error when passing a function as props to a component. But I can't figure out what's going on. Thanks in advance
TaskForm
import { useState } from "react";
function TaskForm(createTask) {
const [title, setTitle] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
const newTask = {
title,
};
createTask(newTask);
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder="Escribe tu tarea"
onChange={(e) => setTitle(e.target.value)}
/>
<button>Guardar</button>
</form>
);
}
export default TaskForm;
App
import TaskList from "./TaskList";
import TaskForm from "./TaskForm";
import { tasks as data } from "./tasks";
import { useState, useEffect } from "react";
function App() {
const [tasks, setTasks] = useState([]);
useEffect(() => {
setTasks(data);
}, []);
function createTask(task) {
setTasks([...tasks, task]);
}
return (
<>
<TaskForm createTask={createTask} />
<TaskList tasks={tasks} />
</>
);
}
export default App;
Try to get data from props via destructuring as we are getting props as object
import {useState} from 'react'
function TaskForm({ createTask }) {
const [title, setTitle] = useState('');
const handleSubmit = (e) =>{
e.preventDefault();
const newTask = {
title
};
createTask(newTask)
}
return (
<form onSubmit={handleSubmit}>
<input placeholder="Escribe tu tarea"
onChange={(e)=> setTitle(e.target.value)}
/>
<button>
Guardar
</button>
</form>
)
}
or you can try as:
import {useState} from 'react'
function TaskForm(props) {
const { createTask } = props;
const [title, setTitle] = useState('');
const handleSubmit = (e) =>{
e.preventDefault();
const newTask = {
title
};
createTask(newTask)
}
return (
<form onSubmit={handleSubmit}>
<input placeholder="Escribe tu tarea"
onChange={(e)=> setTitle(e.target.value)}
/>
<button>
Guardar
</button>
</form>
)
}
App.js
import { useState } from "react";
import TaskForm from "./component/Comp1";
import TaskList from "./component/Comp2";
import "./styles.css";
export default function App() {
const [tasks, setTasks] = useState([]);
const creteTask = (newTasks) => {
setTasks([...tasks, newTasks]);
};
return (
<div className="App">
<TaskForm creteTask={creteTask} />
<TaskList tasks={tasks} />
</div>
);
}
TaskForm.js
import { useState } from "react";
export default function TaskForm({ creteTask }) {
const [title, setTitle] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
creteTask(title);
setTitle("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
placeholder="Escribe tu tarea"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<button>Guardar</button>
</form>
</div>
);
}
TaskList.js
export default function TaskList({ tasks }) {
console.log(tasks);
return (
<div>
<h3>Tasks</h3>
{tasks.map((task, i) => (
<p key={i}>{task}</p>
))}
</div>
);
}

Modal reseting the redux state

I made a react/redux application using firebase, but when i open the modal and close it or add a product all data of application are set hidden
App.js file
import React, { useState } from "react";
import "./App.css";
import Layout from "./components/Layout/Layout";
import Header from "./components/Header/Header";
import FormModal from "./components/Modal/FormModal";
import Modal from "./components/Modal/Modal";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import ProductReducer from "./reducers/ProductReaducer";
import Card from "./components/Card/Card";
function App() {
const store = createStore(ProductReducer);
const [showModal, setShowModal] = useState(false);
const onShowModal = () => {
setShowModal(true);
};
const onHideModal = () => {
setShowModal(false);
};
return (
<div>
<Provider store={store}>
<Header onShowModal={onShowModal} />
<Layout>
<Card />
</Layout>
<Modal show={showModal}>
<FormModal show={showModal} onHideModal={onHideModal} />
</Modal>
</Provider>
</div>
);
}
export default App;
Modal.js file
import React from "react";
import { useSelector } from "react-redux";
const Modal = (props) => {
const states = useSelector((state) => state);
console.log(states);
return (
<div
className={
props.show
? `bg-slate-400 fixed top-0 left-0 w-screen h-screen flex justify-center items-center z-1`
: "hidden"
}
>
{props.children}
</div>
);
};
export default Modal;
FormModal.js file
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { addItem } from "../../actions/ProductActions";
import { collection, addDoc, getFirestore } from "firebase/firestore";
const FormModal = (props) => {
const [infoProduct, setInfoProduct] = useState({
title: "",
url: "",
description: "",
price: 0,
});
const handleChange = (evt) => {
const targetValue = evt.target.value;
const key = evt.target.name;
setInfoProduct((old) => ({
...old,
[key]: targetValue,
}));
};
const dispatch = useDispatch();
const db = getFirestore();
const addProduct = async () => {
try {
const docRef = await addDoc(collection(db, "webjump-store"), productItem);
dispatch(addItem(infoProduct));
console.log("Document written with ID: ", docRef.id);
} catch (e) {
console.error("Error adding document: ", e);
}
};
const productItem = {
produto: {
title: infoProduct.title,
url: infoProduct.url,
description: infoProduct.description,
price: infoProduct.price,
},
};
return (
<div className={props.show ? "bg-slate-300 w-64 p-6 z-1" : "hidden"}>
{" "}
<button type="button" className="float-right" onClick={props.onHideModal}>
X
</button>
<form className="flex flex-col items-center">
<label>Title</label>
<input
className="my-3"
type="text"
name="title"
onChange={handleChange}
/>
<label>Url</label>
<input
className="my-3"
type="text"
name="url"
onChange={handleChange}
/>
<label>Description</label>
<input
className="my-3"
type="text"
name="description"
onChange={handleChange}
/>
<label>Price</label>
<input
className="my-3"
type="text"
name="price"
onChange={handleChange}
/>
<button
className="my-4 p-4 bg-slate-200 border-2 border-solid border-black"
type="button"
onClick={addProduct}
>
Add Product
</button>
</form>
</div>
);
};
export default FormModal;
I have no ideia o it can be.
When i do a console.log in my redux state it appers to be empty.
But when i reload the page all data from fire store appears again.

Share the useState between two adjacent components in React

I need help, is there any possible way to send the useEffect submittedInput from search.js to AllThemeContext.js to use it as value of Provider ? Both are in two separated files.
Please I asked this question and none has responded please help me.
I don't want to move the search to context i want them to stay in separated files.
/Search js/
/*Import*/
import React, { useState } from "react";
import "./Search.scss";
/*Component*/
const Search = () => {
const [input, setInput] = useState("");
const [submittedInput, setSubmittedInput] = useState("");
const onFormSubmit = (e) => {
e.preventDefault();
setInput("");
};
return (
<>
<div className="Search">
<form onSubmit={onFormSubmit} className="Search__form">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
type="text"
placeholder=" Title, companies, expertise, or benefits"
style={{ fontFamily: "Poppins, FontAwesome" }}
></input>
<button onClick={() => setSubmittedInput(input)}>Search</button>
</form>
</div>
</>
);
};
export default Search;
AllThemeContext:
import React, { createContext, useState } from "react";
export const AllContext = createContext();
const AllContextProvider = (props) => {
const [input, setInput] = useState();
const [numbs, setNumbs] = useState(1);
return (
<AllContext.Provider value={{ input, numbs }}>
{props.children}
</AllContext.Provider>
);
};
export default AllContextProvider;

Not getting pre-filled state value from redux when user try to edit the form?

I am trying to get the state from redux store and trying to fill the input field from state. If user in edit mode. In edit mode, we normally show the prefilled value in input field. But what is wrong with the below approach?
I am able to store single user successfully in reducer but in component i am not getting. Sometimes i get the value. Overall, it's very inconsistent.
import React, { useState, useEffect } from "react";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import { useSelector, useDispatch } from "react-redux";
import { useHistory, useParams } from "react-router-dom";
import { addUser, getSingleUser, updateUser } from "../redux/actions";
const useStyles = makeStyles((theme) => ({
root: {
marginTop: 100,
"& > *": {
margin: theme.spacing(1),
width: "45ch",
},
},
}));
const initialState = {
name: "",
address: "",
contact: "",
email: "",
};
const EditUser = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.users);
console.log("user", user);
const [state, setState] = useState(user);
const [error, setError] = useState("");
const { name, address, email, contact } = state;
const classes = useStyles();
const history = useHistory();
let dispatch = useDispatch();
const onInputChange = (e) => {
let { name, value } = e.target;
setState({ ...state, [name]: value });
};
useEffect(() => {
dispatch(getSingleUser(id));
}, []);
const handlSubmit = (e) => {
e.preventDefault();
console.log("name", name);
if (!name || !email || !address || !contact) {
setError("Please fill all Input Field");
} else {
dispatch(updateUser(state, id));
setError("");
history.push("/");
}
};
return (
<>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
type="submit"
color="secondary"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit user</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form
className={classes.root}
noValidate
autoComplete="off"
onSubmit={handlSubmit}
>
<TextField
id="standard-basic"
label="Name"
value={name}
name="name"
onChange={onInputChange}
type="text"
/>
<br />
<TextField
id="standard-basic"
value={email}
name="email"
label="Email"
type="email"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
value={contact}
name="contact"
label="Contact"
type="number"
onChange={onInputChange}
/>
<br />
<TextField
id="standard-basic"
label="Address"
value={address}
name="address"
type="text "
onChange={onInputChange}
/>
<br />
<Button
style={{ width: "100px" }}
variant="contained"
type="submit"
color="primary"
>
Update
</Button>
</form>
</>
);
};
export default EditUser;
Below is redux actions logic to get the single user and dispatching an action to store single user value in reducer.
export const getSingleUser = (id) => {
return function (dispatch) {
axios
.get(`${process.env.REACT_APP_API}/${id}`)
.then((resp) => {
console.log("resp", resp);
dispatch(singleUser(resp.data));
})
.catch((error) => console.log(error));
};
};

Setting the value of DatePicker (from antd) in react-hook-form

I'm trying to figure out how to use the DatePicker from antd with react-hook-form.
Currently, my attempt is:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useForm from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { Input as InputField, Form, Button, DatePicker, Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
import Select from "react-select";
const { Content } = Layout
const { Text, Paragraph } = Typography;
const { Meta } = Card;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
const Team = props => {
const { register, handleSubmit, setValue, errors } = useForm();
const [ dueDate, setDate ] = useState(new Date());
const [indexes, setIndexes] = React.useState([]);
const [counter, setCounter] = React.useState(0);
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalBudget");
};
// const handleChange = dueDate => setDate(date);
const handleChange = (e) => {
setValue("dueDate", e.target.value);
}
const onSubmit = data => {
console.log(data);
};
const addMilestone = () => {
setIndexes(prevIndexes => [...prevIndexes, counter]);
setCounter(prevCounter => prevCounter + 1);
};
const removeMilestone = index => () => {
setIndexes(prevIndexes => [...prevIndexes.filter(item => item !== index)]);
};
const clearMilestones = () => {
setIndexes([]);
};
useEffect(() => {
register({ name: dueDate }); // custom register antd input
}, [register]);
Note: i have also tried name: {${fieldName}.dueDate - that doesn't work either.
return (
<div>
<HeaderBranding />
<Content
style={{
background: '#fff',
padding: 24,
margin: "auto",
minHeight: 280,
width: '70%'
}}
>
<form onSubmit={handleSubmit(onSubit)}>
{indexes.map(index => {
const fieldName = `milestones[${index}]`;
return (
<fieldset name={fieldName} key={fieldName}>
<label>
Title:
<input
type="text"
name={`${fieldName}.title`}
ref={register}
/>
</label>
<label>
Description:
<textarea
rows={12}
name={`${fieldName}.description`}
ref={register}
/>
</label>
<label>When do you expect to complete this milestone? <br />
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
</DatePicker>
</label>
<Button type="danger" style={{ marginBottom: '20px', float: 'right'}} onClick={removeMilestone(index)}>
Remove this Milestone
</Button>
</fieldset>
);
})}
<Button type="primary" style={{ marginBottom: '20px'}} onClick={addMilestone}>
Add a Milestone
</Button>
<br />
<Button type="button" style={{ marginBottom: '20px'}} onClick={clearMilestones}>
Clear Milestones
</Button>
<input type="submit" value="next - budget" />
</form>
</Content>
</div>
);
};
export default withRouter(Team);
This generates an error that says: TypeError: Cannot read property 'value' of undefined
setValue is defined in handleChange.
I'm not clear on what steps are outstanding to get this datepicker functioning. Do I need a separate select function?
Has anyone figured out how to plug this datepicker in?
I have also tried:
const handleChange = (e) => {
setValue("dueDate", e.target.Date);
}
and I have tried:
const handleChange = (e) => {
setValue("dueDate", e.target.date);
}
but each of these generations the same error
I have built a wrapper component to work with external controlled component easier:
https://github.com/react-hook-form/react-hook-form-input
import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
function App() {
const { handleSubmit, register, setValue, reset } = useForm();
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<RHFInput
as={<Select options={options} />}
rules={{ required: true }}
name="reactSelect"
register={register}
setValue={setValue}
/>
<button
type="button"
onClick={() => {
reset({
reactSelect: '',
});
}}
>
Reset Form
</button>
<button>submit</button>
</form>
);
}
try this out, let me know if it makes your life easier with AntD.
/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import { DatePicker } from 'antd';
import { Controller } from 'react-hook-form';
import color from '../../assets/theme/color';
import DatePickerContainer from './DatePickerContainer';
function DatePickerAntd(props) {
const { control, rules, required, title, ...childProps } = props;
const { name } = childProps;
const [focus, setFocus] = useState(false);
const style = {
backgroundColor: color.white,
borderColor: color.primary,
borderRadius: 5,
marginBottom: '1vh',
marginTop: '1vh',
};
let styleError;
if (!focus && props.error) {
styleError = { borderColor: color.red };
}
return (
<div>
<Controller
as={
<DatePicker
style={{ ...style, ...styleError }}
size="large"
format="DD-MM-YYYY"
placeholder={props.placeholder || ''}
onBlur={() => {
setFocus(false);
}}
onFocus={() => {
setFocus(true);
}}
name={name}
/>
}
name={name}
control={control}
rules={rules}
onChange={([selected]) => ({ value: selected })}
/>
</div>
);
}
export default DatePickerAntd;
my container parent use react-hooks-form
const { handleSubmit, control, errors, reset, getValues } = useForm({
mode: 'onChange',
validationSchema: schema,
});
<DatePickerAntd
name="deadline"
title={messages.deadline}
error={errors.deadline}
control={control}
required={isFieldRequired(schema, 'deadline')}
/>
like that, its working for me ;-)
Try this:
<DatePicker
selected={ dueDate }
// ref={register}
InputField name={`${fieldName}.dueDate`}
onChange={()=>handleChange(index)}
//onChange={ handleChange }
>
<input
type="date"
name={`${fieldName}.dueDate`}
inputRef={register}
/>
It looks like if you are using onChange={handleChange(index)} it does not pass a function instead you are passing an execution result of that function.
And if you are trying to access event inside handleChange, you should manually pass if from binding scope otherwise, it will be undefined.
onChange={()=>handleChange(index, event)}

Categories

Resources