How to insert data with post using react redux? - javascript

I am learning react-redux, so I am creating a CRUD app for users using JSON placeholder API, now I am able to display data, delete and edit, but I have a problem with adding data.
Here is a live demo in the sandbox : redux live demo
Now when I click add user button I get the following error.
Cannot read property 'editing' of undefined.
Here is my form component to add a user.
import React, { useState } from 'react'
import {useDispatch} from 'react-redux'
import {addUser, addNewUser } from './redux/acitons/users/Users';
function AddUserForm({ user }) {
const dispatch = useDispatch();
const [name, setName ] = useState(user.name);
const handleSubmit = () =>{
console.log(name)
dispatch(addNewUser( {
...user, name
}));
}
const handleCancel = () => {
console.log(user.id);
dispatch(addUser(user.id))
}
return (
<tr>
<td>{user.id}</td>
<td>
<input
defaultValue={user.name}
onChange={e => setName(e.target.value)}
/>
</td>
<td>
<button type="button" className="btn outline" onClick={handleCancel}>
<i className="material-icons">Cancel</i>
</button>
<button
type="button"
className="btn btn-success btn-link"
onClick={handleSubmit}
>
<i className="material-icons">save</i>
</button>
</td>
</tr>
);
}
export default AddUserForm
What do I need to do to solve the problem?, any help or suggestions will be appreciated, the live demo can be found here in the sandboxredux demo

Looking at your code - it seems like your app is breaking since you are not passing any user-payload object to theaddUser dispatch call(on User.js line 64).
Here's a possible way to solve this:
Passing a new-user payload object with an id 1 higher than the previous user on the list) and then dispatch an editUser for that new id.
const addNewUser = () => {
const usersList = userData.users;
const newUserId = usersList[usersList.length - 1].id + 1;
addUser({
id: newUserId
});
editUser(newUserId);
};
This is a very simplified solution (for example - a better approach would be to give a unique id to each user and index the list on the client not based on the server ids) - but it should give you an idea of how to take this forward.

Well first of all, to temporary handle compile errors put question marks on user?.editing and other places where you use user.id or other parameters.
Other thing, I can see that you are using id for editing and deleting so you should also set id property in your userForm

Related

Filter an object from an array in react

I am fetching data from an API using axios.
On my invoice details page when I try to get data of only one invoice using this code
const id = props.match.params.id;
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id));
It returns an object or undefined but I only want an object inside an array or an empty array not undefined how should I do that?
When I tried to use .filter method instead of .find, it logged the array into the console infinite time.
Complete code:
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import backIcon from '../assets/images/icon-arrow-left.svg'
import InvoiceDetailsHeader from './InvoiceDetailsHeader';
import { useSelector } from 'react-redux';
// remove this after adding DB
import data from '../data.json'
import InvoiceCardDetails from './InvoiceCardDetails';
const InvoiceDetails = (props) => {
const [invoiceData, setInvoiceData] = useState([]);
const id = props.match.params.id;
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id));
useEffect(() => {
setInvoiceData(invoice);
// console.log(invoiceData)
}, [id, invoice]);
return (
<div className="mx-auto px-12 py-16 w-full max-w-3xl">
<Link to="/" className="text-neutral text-xs"><img className="inline -mt-1 mr-4" src={backIcon} alt="back" /> Go back</Link>
<InvoiceDetailsHeader data={invoiceData} />
<InvoiceCardDetails data={invoiceData} />
</div>
)
}
export default InvoiceDetails
Anyone please help me with this.
I think it's because you're setting setInvoiceData(invoice) which is undefined at the very start. so make a check on it
if(invoice){
setInvoiceData([invoice])
}
please try this one
useEffect(() => {
if(invoice){
setInvoiceData([...invoiceData, invoice])
}
}, [id, invoice]);
First of all, I don't know if I missed anything, but I don't think it's a good way for invoice to be possible for both objects and empty array. I think a better way is to divide the conditions and render the invoice when the ID is not found.
If a filter method is used instead of a find, the filter method returns a new array instance each time. So as the second argument(invoice) of use Effect changes, the update callback of use Effect will continue to be repeated.
const invoice = useSelector((state) => state.invoices.find(invoice => invoice._id === id) ?? []);
What you want can be done simply using Nullish coalescing operator.
However, [] also creates a new array instance, so update callback is repeated indefinitely.
So to make what you want work in the current code, please remove the invoice from the dependence of useEffect as below.
useEffect(() => {
setInvoiceData(invoice);
// console.log(invoiceData)
}, [id]);

How to trigger requests with a button using React-query?

I have been trying to learn React-query but can't seem to trigger requests with my onSubmit event. Right now the code is sending the request with "washington" as the default parameter and printing it to the screen, and a new request also triggers with the onBlur event, and fetch the data if the city typed is valid.
The thing is that wish I could move this logic to the submit() function, treat the data on the input and only if the data is valid, proceed to make the request. This is the stackblitz where I reproduced the problem with a free apiKey: StackBlitz
This is the code:
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const Fetch = async city => {
let apiKey = '91b5ff77e9e7d1985a6c80bbbb3b2034';
const { data } = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
return data;
};
const Weather = () => {
const [city, setCity] = useState('washington');
const { data, error } = useQuery(['temperature', city], () => Fetch(city));
const submit = () => {};
return (
<div>
<form onSubmit={submit}>
<input onBlur={e => setCity(e.target.value)} type="text" />
<button type="submit">send</button>
</form>
{!data ? null : <div>{data.main.temp}</div>}
</div>
);
};
export default Weather;
You can also call setCity in the onSubmit event of the form, as the onSubmit event gets the complete submitted form in the submit event:
<form
onSubmit={(event) => {
event.preventDefault();
const city = new FormData(event.currentTarget).get("city");
// do validation here
if (isValid(city)) {
setCity(city)
}
>
<input name="city" type="text" />
<button type="submit">send</button>
</form>
make sure to give your input a name so that you can grab it from the form submit event.
You can use useMutation hooks. As what the documentation said mutations are typically used to create/update/delete data or perform server side-effects. For this purpose, React Query exports a useMutation hook.. This hooks will return an object that gives you a mutation function that you can use to trigger request based on user interactions.
const { mutate: renamedMutationFunction } = useMutation(newTodo => axios.post('/todos', newTodo)).
Then somewhere in your code, you can do:
const handleClick = () => { renamedMutationFunction(); //invoking the mutation }
EDIT
see #TkDodo answer for better solution. You can basically just re-set the city, and react-query will automatically refetch the data.

Trying to create a multi-functional button using react js {on first click - saves data, turns "view", and on second click - navigate to saved page}

I am trying to create a multi-functional button in React Js (MERN STACK), which is Initially a "Save" button. On click, it should ideally post the row data to MongoDB, once the data is being stored and 200 response status is achieved, it should convert into "VIEW" Button, which will have a link to navigate to View the Saved data Page. I have tried numerous ways, but not able to achieve the required target. What should I do? Your help is really appreciable. Thank You SO much in advance.....!
[Image Link below]
import React, { useState } from 'react';
import axios from 'axios';
export default function SaveButton( {cTag} ) {
// cTag prop holds the values of row data in object format as shown in the given figure.
console.log(cTag);
const [saveBtn, setViewBtn] = useState("Save");
const {name, symbol, mCap, cVal} = cTag;
const SaveCompany = () => {
const companyData = {
name:name,
symbol:symbol,
mCap: mCap,
cVal: cVal
}
axios.post('http://localhost:8082/api/books', companyData).then((response) => {
console.log(response.status);
setViewBtn("View");
window.alert("Data Submitted Successfully...!")
}).catch(() => {
alert("Data Didnt Save");
})
}
return (
<>
<button className = "btn btn-sm btn-primary" onClick = {SaveCompany}> {saveBtn} </button>
</>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
dont call saveCompany from button, you would need another 2 functions, the view and the condition:
const handleClick = () => {
switch(saveBtn) {
case "Save" : saveCompany(); break;
case "View" : viewCompany(); break;
}
}
...
<button className = "btn btn-sm btn-primary" onClick = {handleClick}> {saveBtn} </button>

How to edit / update values in CRUD application in ReactJS

Trying to add the ability for, when a user adds an item to the table, to edit the values once they hit the edit button.
Code under Edit Project below in my code isn't working. I've tried to research a bunch of different ways on how to do this, but I keep coming up short.
Any idea on how I can do this easily?
Link to codesandbox.
MainCrud.js
import React, { useState } from "react";
import CrudIntro from "../crud/crudIntro/crudIntro";
import CrudAdd from "../crud/crudAdd/crudAdd";
import CrudTable from "../crud/crudTable/crudTable";
const MainCrud = props => {
// Initiazle project data
const projectData = [];
// Initial state for edit button's hidden state to be false
const [editBtn, setEditBtn] = useState(false);
// Initialize state of the project data above to 'projects'
const [projects, setProject] = useState(projectData);
// Add Project
const addProject = project => {
// Create new item with a unique ID
project.id = projects.length + 1;
// Take the current list, and add onto it via the spread operator
setProject([...projects, project]);
};
// Delete Project
const deleteProject = id => {
setProject(projects.filter(project => project.id !== id));
};
// Edit Project
const editProject = e => {
setEditBtn(true);
this.setState({
...this.state.name,
...this.state.description,
...this.state.date,
[e.target.name]: e.target.value
});
};
return (
<div>
<section id="add">
<CrudIntro title={props.title} subTitle={props.subTitle} />
<CrudAdd addProject={addProject} />
</section>
<section id="main">
<CrudTable
projectData={projects}
editProject={editProject}
deleteProject={deleteProject}
/>
</section>
</div>
);
};
export default MainCrud;
I see you are trying to do this.setState in a functional component, which you can't do. this will point to undefined in this case

Modularizing code in React/Redux

The main question
I am used to using React with ES6 classes. I am also used to modularizing portions of code into separate functions. I am looking at the following example and trying to figure out how to put the value for onSubmit as a separate function.
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form
onSubmit={e => {
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}}
>
<input
ref={node => {
input = node
}}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
I have tried something like this:
import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions'
function handleSubmit(e){
e.preventDefault()
if (!input.value.trim()) {
return
}
dispatch(addTodo(input.value))
input.value = ''
}
let AddTodo = ({ dispatch }) => {
let input
return (
<div>
<form onSubmit={e => handleSubmit(e)}>
<input ref={node => {input = node }}
/>
<button type="submit">
Add Todo
</button>
</form>
</div>
)
}
AddTodo = connect()(AddTodo)
export default AddTodo
But then of course it does not work as it does not recognize the input variable. I could pass the input variable to the function, but this does not seem like the right way to do it.
Question 2:
I am unfamiliar with what the following piece of code is doing:
let AddTodo = ({ dispatch }) => {
Where exactly is it getting dispatch from? Is the value of dispatch being passed into the anonymous function?
Question 3
The same with the following code:
<input ref={node => {input = node }}
Where is the value of node coming from and why is it being stored into the input variable?
Answer to Question 1
AddTodo is a React stateless functional component (SFC). It is also a function. Within the SFC is defined a variable input. In order for the handleSubmit callback to be able to make use of input, it is necessary that input be in the enclosing scope where handleSubmit is defined or input be passed as an argument to handleSubmit.
Thus, the following two implementations achieve the desired behavior:
const AddTodo = ({dispatch}) => {
let input
const handleSubmit = e => {
...
}
return (
...
onSubmit={handleSubmit}
...
)
and
const handleSubmit = (e, input) => {
...
}
const AddTodo = ({dispatch}) => {
let input
return (
...
onSubmit={e => handleSubmit(e, input)}
...
)
I highly recommend reading the following blog post by Kent Dodds, paying particular attention to the use of classes vs function closures.
Classes, Complexity, and Functional Programming
Answer to Question 2
The connect function from react-redux wraps the AddTodo component. The way in which connect is being called (with no second argument, or any arguments in this particular case) means AddTodo will receive a prop named dispatch.
To better understand how react-redux and the connect function it provides work, have a look at the documentation:
https://github.com/reactjs/react-redux/blob/master/docs/api.md
Answer to Question 3
Refs are built into React. A function passed to the ref prop receives the underlying DOM element as an argument. In this case, the function passed to the ref prop stores a reference to the DOM element in the variable input. This allows the DOM element to be accessed and mutated later by the callback passed to onSubmit (i.e. handleSubmit). See the React documentation for more details on refs:
https://reactjs.org/docs/refs-and-the-dom.html

Categories