I have a page which allows a user to submit a url from which data is scraped. The user is subsequently presented with the filtered data.
Because the scraping takes some time I would like to implement a loader. While the loader class will (hopefully) be relatively straight forward, it's the state for loading which I'm having issues with. The state itself is never updated. Although other state values are such as setFilters.
Body.js
const [searchState, setSearchState] = useState({
searchCriteria: "https://en.wikipedia.org/wiki/2020_Central_Vietnam_floods",
headers:[],
references: []
});
const [filterState, setFilters] = useState({
languageFilter: ""
});
const [loadingState, setLoadingState] = useState({
loading: false
});
The above are all passed into Search with a context
<>
<SearchContext.Provider value={{searchState, setSearchState,filterState, setFilters, loadingState, setLoadingState}} >
<Search />
<DonateButton />
<WikiHeaderGroup />
</SearchContext.Provider>
</>
And then I have a handleSubmit inside the Search component.
Search.js
import React, {useContext} from "react";
import {SearchContext} from "../../contexts/SearchContext"
import "../../App.css"
export function Search (){
const {searchState, setSearchState, filterState, setFilters, loadingState, setLoadingState} = useContext(SearchContext);
const handleSubmit = (event) => {
setFilters({languageFilter:""})
setLoadingState({loading:true})
console.log("Loading State : " + loadingState.loading)
event.preventDefault();
event.persist(); //persists the event object into the function
const fetchReferences = async () => {
fetch('http://127.0.0.1:8080/search/', {
method: 'POST',
body: JSON.stringify({
url: searchState.searchCriteria
}),
headers: {"Content-type": "application/json; charset=UTF-8"}
}).then(response => {
console.log(response)
return response.json()
}).then(json => {
console.log(json)
setSearchState({
headers:json.headers,
references:json.references
})
setLoadingState({loading:false})
console.log("Loading State : " + loadingState.loading)
});}
fetchReferences();
}
return (
<div className="search container">
<div className="input-group input-group-sm mb-3 center">
<div className="input-group-prepend">
<span className="input-group-text" id="inputGroup-sizing-sm">Wikipedia URL:</span>
</div>
<form onSubmit={(event) => handleSubmit(event)}>
<input
type="text"
id="searchBox"
className="form-control center"
aria-label="Sizing example input"
aria-describedby="inputGroup-sizing-sm"
value={searchState.searchCriteria}
onChange={(event) => setSearchState({searchCriteria:event.target.value, resultId:0})}
placeholder="Add a url" />
</form>
</div>
</div>
);
}
export default Search;
don't use object for booleans, just
const [loadingState, setLoadingState] = useState(false);
....
setLoadingState(true)
btw looks like a closure problem. you see loadingState always false cause the closure.
take a look at this Be Aware of Stale Closures when Using React Hooks
A way to solve it is using refs
const loadingStateRef = useRef(loadingState);
//then inside the function u can access
latestValue.current
Related
I need to be able to parse obj to another component called GuestForm.
However when i try to set obj.first_name i can see in the console that the the obj.first_name value is empty.
On top of having the object empty i would like to parse it to the component.
import React, { Component, useState, useEffect } from 'react';
import GuestForm from '../../components/Guests/GuestForm.js';
import { useParams } from 'react-router-dom';
import axios from "axios";
function Edit() {
const { id } = useParams();
const [mode, setMode] = useState('edit');
const [successMessage, setsuccessMessage] = useState('The guest has been edited successfully!');
const [action, setAction] = useState('/guests/edit');
const obj = {first_name: '', last_name: '', email: '', password: ''};
const headers = {
'Content-Type': 'application/json;charset=UTF-8',
"Access-Control-Allow-Origin": "*",
"Accept": "application/json"
}
const res = fetch(process.env.REACT_APP_API_URL + action, {
method: 'POST',
headers: headers,
body: JSON.stringify({data: {id: id}}),
})
.then((response) => response.json())
.then((responseJson) => {
//return responseJson.json.guest;
obj.first_name = responseJson.json.guest.first_name;
})
.catch((error) => {
console.error(error);
});
console.log(obj); // Empty value for first name here...
return (
<>
<div className="container">
<GuestForm mode={mode} successMessage={successMessage} obj={obj} action={action} />
</div>
</>
);
}
export default Edit;
GuestForm
Here the component GuestForm which should display first name value in the field
import React, { Component, useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
// react-bootstrap components
import {
Button,
Card,
Form,
Row,
Col,
} from "react-bootstrap";
import axios from "axios";
import { toast } from 'react-toastify';
function GuestForm({mode, successMessage, obj, action}) {
const history = useHistory();
const [details, setDetails] = useState([]);
const [loading, setLoading] = useState(false);
const [first_name, setFirstName] = useState(obj.first_name);
const [last_name, setLastName] = useState(obj.last_name);
const [email, setEmail] = useState(obj.email);
const [password, setPassword] = useState(obj.password);
const handleSave = e => {
e.preventDefault();
setLoading(true);
axios({
method: "POST",
url: process.env.REACT_APP_API_URL + action,
headers: { 'Content-Type': 'application/json;charset=UTF-8', "Access-Control-Allow-Origin": "*", "Accept": "application/json" },
data: {
data: obj
}
}).then(result => {
if(result.data.json.error == false) {
toast(successMessage, {
position: "top-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
history.push('/dashboard/guests');
}
setDetails(result.data.json);
setLoading(false);
});
};
return (
<>
<div className="container">
<div class="row">
<div class="col-lg-12">
<h1 className="mt-0 mb-4 green-color">{mode == 'edit'? <span>Edit</span>: 'New' } Guest</h1>
</div>
</div>
<Form onSubmit={handleSave} autoComplete="off">
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2">
<Form.Group>
<label htmlFor="exampleInputEmail1">
Email Address
</label>
<Form.Control
value={email}
onChange={e => setEmail(e.target.value)}
type="email"
autoComplete="off"
></Form.Control>
</Form.Group>
</div>
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2">
<Form.Group>
<label>Password</label>
<Form.Control
value={password}
onChange={e => setPassword(e.target.value)}
type="password"
autoComplete="new-password"
></Form.Control>
</Form.Group>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2">
<Form.Group>
<label>First Name</label>
<Form.Control
value={first_name}
onChange={e => setFirstName(e.target.value)}
type="text"
autoComplete="off"
></Form.Control>
</Form.Group>
</div>
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2">
<Form.Group>
<label>Last Name</label>
<Form.Control
value={last_name}
onChange={e => setLastName(e.target.value)}
type="text"
autoComplete="off"
></Form.Control>
</Form.Group>
</div>
</div>
{(details.guest && details.error ) && <div className="error-message mt-4 mb-1">{details.message}</div>}
<Button
className="btn-fill pull-right mt-3"
type="submit"
variant="info"
disabled={loading}
>
{loading && <span>{mode == 'edit'? <span>SAVE CHANGES</span>: 'ADD' }...</span>}
{!loading && <span>{mode == 'edit'? <span>SAVE CHANGES</span>: 'ADD' }</span>}
</Button>
<div className="clearfix"></div>
</Form>
</div>
</>
);
}
export default GuestForm;
The reason your console.log is showing up as empty is because you are setting the value of obj.first_name in an asynchronous callback, but the actual logging line will be executed synchronously before that asynchronous callback is called. If you were to instead add another .then to the chain and do the console.log in there, you would see the updated value. Here's a snippet that demonstrates what I mean:
const obj = { a: 'b' };
Promise.resolve()
.then(() => {
obj.a = 'c';
})
.then(() => {
console.log('async:', obj);
});
console.log('sync:', obj);
If you want to send this value to GuestForm, you'll have to use a state variable that will be updated once the fetch call finishes. You also want to wrap this fetch call in a useEffect, so that calling setObj doesn't result in an endless loop (the fetch call causes the state update, which then causes the component to be re-rendered, which causes the fetch call to rerun, and so on). Something like:
import React, { Component, useState, useEffect } from 'react';
import GuestForm from '../../components/Guests/GuestForm.js';
import { useParams } from 'react-router-dom';
import axios from "axios";
function Edit() {
const { id } = useParams();
const [mode, setMode] = useState('edit');
const [successMessage, setsuccessMessage] = useState('The guest has been edited successfully!');
const [action, setAction] = useState('/guests/edit');
const [obj, setObj] = useState({first_name: '', last_name: '', email: '', password: ''});
const headers = {
'Content-Type': 'application/json;charset=UTF-8',
"Access-Control-Allow-Origin": "*",
"Accept": "application/json"
}
useEffect(() => {
const res = fetch(process.env.REACT_APP_API_URL + action, {
method: 'POST',
headers: headers,
body: JSON.stringify({data: {id: id}}),
})
.then((response) => response.json())
.then((responseJson) => {
//return responseJson.json.guest;
const newObj = { ...obj, first_name:
responseJson.json.guest.first_name };
setObj(newObj);
})
.catch((error) => {
console.error(error);
});
}, []);
console.log(obj); // This will now show the updated value (but will still have the default value on the initial render)
return (
<>
<div className="container">
<GuestForm mode={mode} successMessage={successMessage} obj={obj} action={action} />
</div>
</>
);
}
export default Edit;
To use the updated value in GuestForm, you need to make sure your state variable is updated when the passed in prop is updated. This is best achieved with a useEffect. Add this to your GuestForm component
useEffect(() => {
setFirstName(obj.first_name);
}, [obj]);
This is necessary because you're duplicating the prop value with state variables in the child component. A more common pattern would be to pass both obj and setObj as props to GuestForm so that in the child you can modify the parent's state variable directly without creating a copy
i am trying to make a CRUD app in DRF-Reactjs by following Tania rascia's example
i have successfully implemented add, delete, list view. but i am trying to edit a specific row which is not updating in DRF backend. but the edited row is shown in the frontend list. why it is not updating in django admin list?
in DRF side views.py:
#api_view(['POST'])
def TodoUpdate(request, pk):
todo = Todo.objects.get(id=pk)
serializer = TodoSerializer(instance=todo, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
i am using cors header to interface between frontend to backend. here is the frontend code for edit:
App.js:
import React,{Fragment, useState,useEffect} from 'react'
import EditList from './components/EditList';
import axios from 'axios'
export default function App() {
const initialTodoSate = { id: null, title: "", body: "" };
const [todos, setTodos] = useState([]);
const [todoList, setTodolist] = useState(initialTodoSate);
const [editing, setEditing] = useState(false);
useEffect(()=>{
axios.get("http://localhost:8000/api/todo-list",{})
.then(res=>{
setTodos(res.data)
}).catch(err=>{
console.log(err)
})
},[])
const addTodoList = (todo) => {
axios
.post("http://localhost:8000/api/todo-create/",todo)
.then((res) => {
console.log(res.data);
todo.id = todos.length + 1;
setTodos([todo, ...todos]);
})
.catch((err) => {
console.log(err);
});
};
const deleteTodo = (id) => {
setEditing(false);
axios.delete(`http://localhost:8000/api/todo-delete/${id}/`)
.then(res=>{
setTodos(todos.filter((todo) => todo.id !== id));
}).catch(err=>{
console.log(err)
})
};
const updateTodo = ( id,updatedTodo) => {
axios
.post(`http://localhost:8000/api/todo-update/${id}/`, id)
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
setEditing(false);
setTodos(todos.map((todo) => (todo.id === id ? updatedTodo : todo)));
};
const editRow = (todo) => {
setEditing(true);
setTodolist({
id: todo.id,
title: todo.title,
description: todo.description,
});
};
return (
<div className="container">
<h1>Django-based Todo with React Hooks</h1>
{editing ? (
<Fragment>
<h3>Edit Task</h3>
<EditList
editing={editing}
setEditing={setEditing}
todoList={todoList}
updateTodo={updateTodo}
/>
</Fragment>
) : (
<Fragment>
<CreateTodo addTodoList={addTodoList} />
<hr />
</Fragment>
)}
<div className="flex-row">
<div className="flex-large">
<TodoList todos={todos} editRow={editRow} deleteTodo={deleteTodo} />
</div>
</div>
</div>
);
}
and EditList.js:
import React, { useState,useEffect } from "react";
export default function EditList({ todoList, setEditing, updateTodo }) {
const [todo, setTodo] = useState([todoList]);
useEffect(() => {
setTodo(todoList);
}, [todoList]);
const handleChange = (e) => {
const { name, value } = e.target;
setTodo({ ...todo, [name]: value });
};
return (
<form
onSubmit={(e) => {
e.preventDefault();
updateTodo(todo.id, todo);
}}
>
<label>Title:</label>
<br />
<input
type="text"
name="title"
value={todo.title}
onChange={handleChange}
/>
<br />
<label>Description:</label>
<br />
<input
type="text"
name="body"
value={todo.body}
onChange={handleChange}
/>
<br />
<button>Update Task</button>
<button onClick={() => setEditing(false)} className="button muted-button">
Cancel
</button>
</form>
);
}
when i try to edit one row with title and body, it is edited and after pressing the update button, the updated row included in the list. but the problem is when i look into the django admin it has not been updated and when i check the development tools, i found an error:
Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
at input
at form
at EditList (http://localhost:3000/static/js/main.chunk.js:511:3)
at div
at App (http://localhost:3000/static/js/main.chunk.js:70:83)
console. # vendors~main.chunk.js:31671
where am i having the mistake?
can anyone help me please? please let me know if you need any additional codes or information.
Trying to update something should be done in a put request, not a post request. This is a REST API convention, but a discrepancy may have some consequence down the line.
In this case, the error in your development tools is telling you that one of your components has an onChange/onSubmit etc property that is changing over the course of one mount from null to a function. This is not what's causing your issue, but I suspect it can be fixed by declaring the code in a handleSubmit function and then putting that into your onSubmit.
I think the error that's actually causing your problem is that the updatedTodo is not being sent to the backend. All that is being sent is the id (second parameter of axios.post). So if you pause the backend during execution, you would see that request.data = the id only, when it should be TodoSerializer's readable fields.
PS:
You can add a "debugger;" statement in the code after the updateToDo async request error to see what the error actually is (read more on the development tools debugging - browser dependent).
Don't abuse fragments - in this case, it would make for a more accessibility-friendly experience if you use divs in most of these components. Wouldn't it make more sense if the heading of some content was grouped with the content? https://developers.google.com/web/fundamentals/accessibility/focus/dom-order-matters
So, I'm trying to pass data from an input element into an async function within my React App.js file. I'm having trouble understanding how to push the input value into the callAPI function.
At the moment I just have a dummy/placeholder ipaddress within the callAPI inorder to test the button is working and calling the function onClick. Here's my code..
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: '' };
}
async callAPI() {
const ipaddress = '8.8.8.8';
const api_url = `http://localhost:9000/ipdata/${ipaddress}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const json = await res.json();
console.log(json);
document.getElementById('city').textContent = json.city;
document.getElementById('state').textContent = json.region_code;
document.getElementById('zip').textContent = json.zip;
}
render() {
return (
<div className="App">
<h1>IP Search</h1>
<input type="text"></input>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span id="city" /> <span id="state" /> <span id="zip" />
</p>
</div>
);
}
}
export default App;
There are two issues:
To get the input value, use a controlled component: put the input value into state and add a change handler.
To set the city, state, zip sections, don't use vanilla DOM methods (which should be avoided in React in 95% of situations) - instead, put the response into state.
class App extends React.Component {
constructor(props) {
super(props);
this.state = { apiResponse: '', inputValue: '', result: {} };
}
async callAPI() {
try {
const api_url = `http://localhost:9000/ipdata/${this.state.inputValue}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const result = await res.json();
this.setState({ result });
} catch (error) {
// handle errors - don't forget this part
}
}
render() {
return (
<div className="App">
<h1>IP Search</h1>
<input
type="text"
value={this.state.inputValue}
onChange={e => this.setState({ inputValue: e.target.value })}
/>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span>{this.state.result.city}</span>
<span>{this.state.result.state}</span>
<span>{this.state.result.zip}</span>
</p>
</div>
);
}
}
you can store the value of input field inside state and use it directly inside async call.
Plus you need a onchange handler as every time you update input text, state should know the updted value.
import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
// HERE.........................
this.state = { apiResponse: '', text : null };
}
// HERE ...........................
handleChnage = (e) => this.setState({text : e.target.value})
async callAPI() {
// Checking the input value and pass to api..................
console.log(this.state.text)
const ipaddress = '8.8.8.8';
const api_url = `http://localhost:9000/ipdata/${ipaddress}`;
const res = await fetch(api_url, {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
const json = await res.json();
console.log(json);
// Don't use it..............use state to pass the data
document.getElementById('city').textContent = json.city;
document.getElementById('state').textContent = json.region_code;
document.getElementById('zip').textContent = json.zip;
}
render() {
// Here on Input element .................
return (
<div className="App">
<h1>IP Search</h1>
<input type="text" value={this.state.text} onChange={this.handleChange}></input>
<button onClick={this.callAPI}>Search IP</button>
<p>
<span id="city" /> <span id="state" /> <span id="zip" />
</p>
</div>
);
}
}
export default App;
Note - don't use imperative methods like getElementById and others in React.
Please avoid using DOM methods in Reactjs, here is an example of what you might want to do with your application.
`
import React,{useState} from 'react';
function App(){
const [apiRes,setApiRes]= useState('');
const [loading,setLoadng]= useState(false);
const callAPI= async()=>{
// supose this is your api response in json
const hello={
city:"city1",
region_code:"region#123",
zip:"00000"
}
// loading while city and zip are not available
setLoadng(true)
await setTimeout(()=>{setApiRes(hello)},5000)
}
return (
<div className="App">
<h1>IP Search</h1>
<input type="text"></input>
<button onClick={callAPI}>Search IP</button>
{!apiRes && loading && <p>loading count till 5...</p>}
<p>
{apiRes &&
(
<>
<span> {apiRes.city}</span>
<span> {apiRes.region_code}</span>
<span> {apiRes.zip}</span>
</>
)}
</p>
</div>
);
}
export default App;
`
link to sandbox: [sandbox]: https://codesandbox.io/s/priceless-mclaren-y7d7f?file=/src/App.js/ "click here to run above code"
I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html
My it's super simple but I get stuck.
I need to update an array on MongoDB with fetch PUT
I tested it with postman and works perfectly but my app React + Redux doesn't work
import React, { Fragment, useEffect, useState } from "react";
import PropTypes from "prop-types";
import "materialize-css/dist/css/materialize.min.css";
import M from "materialize-css/dist/js/materialize.min.js";
import config from "react-global-configuration";
import Preloader from "../layout/Preloader";
import { connect } from "react-redux";
import { getColors, updateColors } from "../../redux/actions/settingsActions";
const Settings = ({
setting: { settings, loading },
getColors,
updateColors
}) => {
const [HighPColor, setHighPColor] = useState("");
const [NormalPColor, setNormalPColor] = useState("");
const [LowPColor, setLowPColor] = useState("");
useEffect(() => {
M.AutoInit();
getColors();
//eslint-disable-next-line
}, []);
const onSubmit = () => {
const updColors = {
id: settings[0]._id,
colors: [
{
_id: colorsArray.colors[0]._id,
HighPColor,
NormalPColor,
LowPColor
}
]
};
updateColors(updColors);
M.toast({ html: "Settings updated" });
};
if (loading || settings === null) {
return <Preloader />;
}
const colorsArray = settings[0];
return (
<Fragment>
<div id="color-settings" className="container">
<div className="">
<h4>Set Priorities Colors </h4>
<div className="row">
<div>High Priority</div>
<div className="input-field">
<input
type="text"
name="highPColor"
defaultValue={colorsArray.colors[0].HighPColor}
onChange={e => setHighPColor(e.target.value)}
/>
</div>
</div>
<div className="row">
<div>Normal Priority</div>
<div className="input-field">
<input
type="text"
name="normalPColor"
defaultValue={colorsArray.colors[0].NormalPColor}
onChange={e => setNormalPColor(e.target.value)}
/>
</div>
</div>
<div className="row">
<div>Low Priority</div>
<div className="input-field">
<input
type="text"
name="lowPColor"
defaultValue={colorsArray.colors[0].LowPColor}
onChange={e => setLowPColor(e.target.value)}
/>
</div>
</div>
</div>
<div className="">
<a
href="#!"
onClick={onSubmit}
className="modal-close waves-effect blue btn"
>
Enter
</a>
</div>
</div>
</Fragment>
);
};
Settings.propTypes = {
setting: PropTypes.object.isRequired,
getColors: PropTypes.func.isRequired,
updateColors: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
setting: state.settings
});
export default connect(mapStateToProps, { getColors, updateColors })(Settings);
I take everything from some inputs values that work perfectly
Redux action:
export const updateColors = colors => async dispatch => {
try {
setLoading();
const res = await fetch(`/api/settings/${colors.id} `, {
method: "PUT",
body: JSON.stringify(colors),
headers: {
"Content-Type": "application/json"
}
});
const data = await res.json();
dispatch({
type: UPDATE_COLORS,
payload: data
});
} catch ...
Redux reducer:
case UPDATE_COLORS:
return {
...state,
settings: state.settings.map(setting =>
setting._id === action.payload._id ? action.payload : setting
),
loading: false
};
it gives me back:
UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property `NormalPColor` of 'undefined' or 'null'.
[0] at router.put (C:\Users\Marco\Desktop\React-Course\to-do-list\routes\settings.js:81:7)
This happens despite I commented the line 81
Any Idea of my mistakes?
thanks!
It sounds odd but now works I don't know what I have done but now updates