In React.js,
The name of the room assigned to each home appliance
is got from the backend and displayed
I am trying to check the room to which the home appliance belongs with a radio button.
What I want to achieve is
I want to check (reflect on) the radio button that matches the room name assigned to each home appliance.
Issue/error message
Nowhere is checked like a photograph below.
in DropDownForRoomChangeButton.js
Since I can confirm that the contents are properly contained with console.log(item.item.room_name)
I wonder why it wasn't checked.
DiscoverCondoRoom.js
const DiscoverCondoRoom = () => {
const [devices, setDevices] = useState([]);
const getDevices = async(data) => {
await axios.get('xxx.com',
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
console.log(result.data)
setDevices(result.data.attributes);
})
.catch(err => {
console.log(err);
});
}
useEffect(() => {
getDevices();
},[]);
const keys = [
"camera",
"climate",
"cover",
"light",
"lock",
"sensor",
"switch",
];
const entities = keys
.map((key) => (devices[key] || []).map((e) => ({ ...e, key })))
.flat();
return (
<>
<div className="row mx-auto text-center">
{entities.map((entity, i) => (
<div className="">
<DropDownForRoomChangeBotton item={entity} />
</div>
</div>
}
</>
);
}
export default DiscoverCondoRoom;
DropDownForRoomChangeButton.js
import Dropdown from 'react-bootstrap/Dropdown';
const cookies = new Cookies();
const DropDownForRoomChangeButton = (item) => {
const [devices, setDevices] = useState([]);
const getDevices = async(data) => {
await axios.get('xxx.com',
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
console.log(result.data)
setDevices(result.data.attributes);
})
.catch(err => {
console.log(err);
});
}
const keys = [
"camera",
"climate",
"cover",
"light",
"lock",
"sensor",
"switch",
];
const entities = keys
.map((key) => (devices[key] || []).map((e) => ({ ...e, key })))
.flat();
const roomNames = [...new Set(entities.map((entity) => entity.room_name))];
const [val, setVal] = useState();
console.log(val)
const HomeHandleChange = e => setVal(e.target.value);
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
<button className="btn btn-primary button_table_rightside">Unassigned</button>
</a>
));
useEffect(() => {
getDevices();
setVal(item.item.room_nam)
},[]);
console.log(roomNames)
console.log(item)
console.log(item.item.room_name)
return (
<>
<Dropdown className="room_change_dropdown_top">
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" />
<Dropdown.Menu className="room_change_dropdown">
<Dropdown.Item className="room_change_dropdown_item">
{roomNames.map((room_names, i) => (
<div className="flex_radio">
<input
className="room_change_radio"
type="radio"
value={room_names}
onChange={HomeHandleChange}
checked={val === item.item.room_name}
/>
<p className="drop_down_p">{room_names}</p>
</div>
))}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
);
}
export default DropDownForRoomChangeButton;
HTML code
Based on your comment, I think you were going for something like this:
const homeHandleChange = e => setVal(e.target.name);
<input
...
name={item.item.room_name}
onChange={homeHandleChange}
checked={val === item.item.room_name}
/>
This way you set the state with the actual name of the room, not value, which in this case is meaningless.
Also, you don't need the useEffect for setting the initial state.
useState accepts a parameter for a default value.
So you can simply do:
const [val, setVal] = useState(item.item.room_name);
And then you can remove that line from the useEffect.
Note: Regular function, (unlike components / etc..), should be named in camel-case, not Pascal-case.
So HomeHandleChange, should actually be homeHandleChange like in the example above.
Obviously you can do whatever you choose but it's a matter of basic convention that's worth following IMO.
Related
I am using React-Bootstrap-TypeAhead's latest version in my React project. The main goal is to display the options menu when the user types. The menu is displayed when using the default input component but once I use the render input method for customization the options menu stops showing:
working example
import React, { useState } from 'react';
import { AsyncTypeahead } from 'react-bootstrap-typeahead';
/* example-start */
const BasicExample = ({ key, label }) => {
const [singleSelections, setSingleSelections] = useState([]);
const [multiSelections, setMultiSelections] = useState([]);
const [query, setQuery] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [options, setOptions] = useState([]);
const PER_PAGE = 50;
const SEARCH_URI = 'https://api.github.com/search/users';
function makeAndHandleRequest(query, page = 1) {
return fetch(`${SEARCH_URI}?q=${query}+in:login&page=${page}&per_page=50`)
.then((resp) => resp.json())
.then(({ items, total_count }) => {
/* eslint-disable-line camelcase */
const options = items.map((i) => ({
avatar_url: i.avatar_url,
id: i.id,
login: i.login,
}));
return { options, total_count };
})
.catch((err) => console.log(err));
}
const _handleInputChange = (query) => {
setQuery(query);
};
const _handlePagination = (e, shownResults) => {
const { query } = this.state;
const cachedQuery = this._cache[query];
// Don't make another request if:
// - the cached results exceed the shown results
// - we've already fetched all possible results
if (cachedQuery.options.length > shownResults || cachedQuery.options.length === cachedQuery.total_count) {
return;
}
setIsLoading(true);
const page = cachedQuery.page + 1;
makeAndHandleRequest(query, page).then((resp) => {
const options = cachedQuery.options.concat(resp.options);
// this._cache[query] = { ...cachedQuery, options, page };
setIsLoading(false);
setOptions(options);
});
};
const _handleSearch = (query) => {
setIsLoading(true);
makeAndHandleRequest(query).then((resp) => {
setIsLoading(true);
setOptions(resp?.options || []);
});
};
return (
<>
<AsyncTypeahead
{...{ query, isLoading, options }}
id="async-pagination-example"
labelKey="login"
maxResults={PER_PAGE - 1}
minLength={2}
onInputChange={_handleInputChange}
onPaginate={_handlePagination}
onSearch={_handleSearch}
renderInput={({ inputRef, referenceElementRef, ...inputProps }) => (
<div className="form-group h-64">
<label>Job Category</label>
<div className="input-group">
<input
type="text"
{...inputProps}
ref={(input) => {
inputRef(input);
// referenceElementRef(input);
}}
className="form-control"
placeholder=""
/>
</div>
</div>
)}
paginate
placeholder="Search for a Github user..."
renderMenuItemChildren={(option) => (
<div key={option.id}>
<img
alt={option.login}
src={option.avatar_url}
style={{
height: '24px',
marginRight: '10px',
width: '24px',
}}
/>
<span>{option.login}</span>
</div>
)}
useCache={false}
/>
</>
);
};
/* example-end */
export default BasicExample;
The reason you're not seeing any results rendered is that _handleInputChange is triggering a re-render and resetting the debounced onSearch handler before it can fire.
You can wrap _handleSearch with useCallback to fix that:
const _handleSearch = useCallback((query) => {
setIsLoading(true);
makeAndHandleRequest(query).then((resp) => {
setIsLoading(false);
setOptions(resp?.options || []);
});
}, []);
I have a simple React component and inside of it I am fetching data from a remote API, and I want to console.log it in useEffect. I am trying to do it but nothing doesn't get logged into the console, why? What am I missing here? Here is the component:
import React, { useState, useEffect } from 'react';
import { useLocalization } from '#progress/kendo-react-intl';
import { Card, CardHeader, Avatar, CardTitle, CardSubtitle } from '#progress/kendo-react-layout';
import { guid } from '#progress/kendo-react-common';
import { Scheduler } from './../components/Scheduler';
import { employees } from './../resources/employees';
import { images } from './../resources/images';
import { orders, ordersModelFields } from './../resources/orders';
import { teams } from './../resources/teams';
// const orderEmployees = employees.filter(employee => employee.jobTitle === 'Sales Representative');
// const initialFilterState = { };
// orderEmployees.forEach(employee => {
// if(employee.fullName === 'Wait Peperell') {
// initialFilterState[employee.id] = false;
// } else {
// initialFilterState[employee.id] = true;
// }
// });
const Planning = () => {
const localizationService = useLocalization();
const [filterState, setFilterState] = React.useState(initialFilterState);
const [data, setData] = React.useState(orders);
const [fetchedData, setFetchedData] = React.useState(null);
useEffect(() => {
fetch("https://mocki.io/v1/29b83c0b-1a55-430d-a173-92b3632e04aa")
.then(response => response.json())
// 4. Setting *dogImage* to the image url that we received from the response above
.then(data => setFetchedData(data))
console.log(fetchedData)
},[])
// console.log(fetchedData)
const onDataChange = React.useCallback(
({ created, updated, deleted }) => {
setData(old => old
// Filter the deleted items
.filter((item) => deleted.find(current => current[ordersModelFields.id] === item[ordersModelFields.id]) === undefined)
// Find and replace the updated items
.map((item) => updated.find(current => current[ordersModelFields.id] === item[ordersModelFields.id]) || item)
// Add the newly created items and assign an `id`.
.concat(created.map((item) => Object.assign({}, item, { [ordersModelFields.id]: guid() }))))
},
[]
);
const onEmployeeClick = React.useCallback(
(employeeId) => {
setFilterState({
...filterState,
[employeeId]: !filterState[employeeId]
});
},
[filterState, setFilterState]
);
return (
<div id="Planning" className="planning-page main-content">
<div className="card-container grid">
<h3 className="card-title">{localizationService.toLanguageString('custom.teamCalendar')}</h3>
{
orderEmployees.map(employee => {
return (
<div
key={employee.id}
onClick={() => onEmployeeClick(employee.id)}
style={!filterState[employee.id] ? {opacity: .5} : {}}
>
<Card style={{ borderWidth: 0, cursor: 'pointer'}}>
<CardHeader className="k-hbox" >
<Avatar type='image' shape='circle' size={'large'} style={{
borderWidth: 2,
borderColor: teams.find(({teamID}) => teamID === employee.teamId).teamColor,
}}>
<div className="k-avatar-image" style={{
backgroundImage: images[employee.imgId + employee.gender],
backgroundSize: 'cover',
backgroundPosition: 'center center',
}}
/>
</Avatar>
<div>
<CardTitle style={{color: teams.find(({teamID}) => teamID === employee.teamId).teamColor}}>{employee.fullName}</CardTitle>
<CardSubtitle>{employee.jobTitle}</CardSubtitle>
</div>
</CardHeader>
</Card>
</div>
);
})
}
<div className="card-component" >
<Scheduler
data={data.filter(event => filterState[event.employeeID])}
onDataChange={onDataChange}
modelFields={ordersModelFields}
resources={[
{
name: 'Teams',
data: teams,
field: 'teamID',
valueField: 'teamID',
textField: 'teamName',
colorField: 'teamColor'
}
]}
/>
</div>
</div>
</div>
);
}
export default Planning;
I also tried to place the console.log outside of useEffect but still, nothing gets console.logged.
You need to look how useEffect work, setFetchedData is async.
Create another useEffect only for console.log.
useEffect(() => {
console.log(fetchedData);
},[fetchedData]); // Update at the first render + when fetchedData state change.
You can do it like this
useEffect(() => {
fetch("https://mocki.io/v1/29b83c0b-1a55-430d-a173-92b3632e04aa")
.then((response) => response.json())
// 4. Setting *dogImage* to the image url that we received from the response above
.then((data) => {
setFetchedData(data);
console.log(data);
});
}, []);
or juste create another useEffect that listens to fetchedData change, like this
useEffect(() => {
console.log(fetchedData);
}, [fetchedData]);
this is my implementation for description page and I want to show the related products. Everything works but as soon as I add the map for the related products, the page doesn't load anymore. I have tried adding other elements and that seems to work ok, but the issue is with the map I believe.
this is my router for the frontend
<Route exact path="/product/details/:id">
<ProductDescriptionPage />
</Route>
ProductDescriptionPage.js
import { useEffect, useState } from "react";
import {
MDBCard,
MDBCardTitle,
MDBCardText,
MDBCardBody,
MDBCardImage,
MDBRow,
MDBCol,
} from "mdb-react-ui-kit";
import Header from "../components/Header";
const ProductDescriptionPage = () => {
let id = window.location.pathname;
console.log("id : " + id);
let arr = [];
arr = id.split("/");
console.log(arr[3]);
id = arr[3];
const [product, setProduct] = useState({
name: "",
price: 0,
description: "",
category: "",
quantity: "",
isBestSeller: true,
photoURL: "",
});
const [relatedProducts, setRelatedProducts] = useState(null);
useEffect(() => {
fetch(`http://localhost:5000/products/${id}`)
.then((response) => response.json())
.then((json) => {
setProduct(json.data);
})
.catch((err) => {
console.log(`Error ${err}`);
});
}, []);
useEffect(() => {
fetch(`http://localhost:5000/products/related/${product._id}`)
.then((res) => res.json())
.then((json) => {
setRelatedProducts(json.data);
console.log("relatedProducts " + relatedProducts);
})
.catch((err) => {
console.log(`Error ${err}`);
});
}, []);
return (
<>
<p>heyys</p>
<Header />
<MDBCard style={{ width: "70rem", margin: "auto auto auto auto" }}>
<MDBRow className="g-0 align-items-center">
<MDBCol md="6">
<MDBCardImage
src={product.photoURL}
alt="..."
fluid
style={{ height: "30rem", "object-fit": "cover" }}
/>
</MDBCol>
<MDBCol md="6">
<MDBCardBody>
<MDBCardTitle> {product.name}</MDBCardTitle>
<MDBCardText>
This is a wider card with supporting text below as a natural
lead-in to additional content. This content is a little bit
longer.
</MDBCardText>
<MDBCardText>
<small className="text-muted">Last updated 3 mins ago</small>
</MDBCardText>
<MDBCardText>{product.category} </MDBCardText>
</MDBCardBody>
</MDBCol>
</MDBRow>
</MDBCard>
<p>
{relatedProducts.map((e) => e.name)}
</p>
</>
);
};
export default ProductDescriptionPage;
this is the implementation from backend
router.get("/related/:id", (req, res) => {
productModel
.findById(req.params.id)
.then((product) => {
if (!product) {
res.status(401).json({
error: `there is no such product`,
});
}
let limit = req.query.limit ? parseInt(req.query.limit) : 6;
productModel
.find({ _id: { $ne: product._id } })
.where("category")
.equals(product.category)
.limit(limit)
.then((products) => {
console.log(products);
res.json({
data: products,
});
});
})
.catch((err) => {
res.status(500).json({
error: err,
});
});
}
);
first time when I add the map
after refreshing the browser
There might be three solutions to it,
CASE 1.
You need to change the useState of
const [relatedProducts, setRelatedProducts] = useState(null)
to something like this
const [relatedProducts, setRelatedProducts] = useState([])
and inside the return use "?" to relatedProducts?.map()
CASE 2:
Please check what is the value that you are getting from the backend for the related products by using console.log()
CASE 3:
you are mapping the wrong items like e.name might not exist.
DEFAULT:
please provide us the screenshot of the error so that we can tell you the exact problem.
Thanks.
There seems to be something wrong with the way I update state, as it gets overwritten...
import Servis from "./funkc/servisni";
import React, { useState, useEffect } from "react";
export default function ContactUpdate(props) {
const initialState = {
ime: props.item.Ime,
prezime: props.item.Prezime,
datum: props.item.Datum,
kontakt: props.item.Kontakt,
published: props.item.Published,
id: props.Id,
};
const [theItem, setTheItem] = useState();
const [message, setMessage] = useState();
useEffect(() => {
setTheItem(props.item);
console.log(theItem);
}, []);
const handleInputChange = (event) => {
const { name, value } = event.target;
setTheItem({ ...theItem, [name]: value });
console.log(theItem, props.Id);
};
the problem seems to be in the following:
const updateItem = (theItem) => {
let data = {
Ime: theItem.Ime,
Prezime: theItem.Prezime,
Kontakt: theItem.Kontakt,
Datum: theItem.Datum,
Published: true,
Id: theItem.id,
};
Servis.update(theItem.id, data)
.then(() => {
setMessage("Uspjesno ste izmijenili unos!");
})
.catch((e) => {
console.log(e);
});
};
as visible in the console.log
return (
<div className="container">
{console.log(("theItem", props.Id, theItem))}
{theItem ? (
<div className="edit-form">
<h4>Kontakt</h4>
...
<button type="submit" onClick={updateItem}>
Update
</button>
<p>{message}</p>
</div>
) : (
<div>
<br />
<p>Odaberi jedan broj...</p>
</div>
)}{" "}
</div>
);
}
The call on the updateItem function by clicking on the 'Update' button results in the error : Function DocumentReference .update() called with invalid data. Unsupported field value...
Resolved through being careful about naming variables...
</div>
<ContactUpdate item={item} id={theId} />
</div>
and then
const updateItem = () => {
let data = {
Ime: theItem.Ime,
Prezime: theItem.Prezime,
Kontakt: theItem.Kontakt,
Datum: theItem.Datum,
published: true,
id: props.id,
};
Servis.update(props.id, data)
.then(() => {
setMessage("Uspjesno ste izmijenili unos!");
})
.catch((e) => {
console.log(e);
});
};
This is my picker with a linechart(LChart) component.
<Picker selectedValue = {this.state.value}
onValueChange = {this.onValueChange}
>
<Picker.Item value='' label='Select Device...' />
{devices.map((s, i) => {
return (<Picker.Item label={s} value={s} key={i}/>)
})}
</Picker>
<LChart data={Fdata} title={"LineChart"}/>
I have an array of devices to feed into picker and with every onValuechange i want to show the linechart of respective device.
My onValueChange function:
onValueChange = async(value) => {
this.setState({ value: value })
this.interval = setInterval(() => {
var Fdata = [];
fetch('http://ec2-137.compute-1.amazonaws.com:3009/chart/unitID', {
method: 'post',
headers:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
unitID: value,
}) })
.then((response) => response.json())
.then((responseJson) => {
responseJson.forEach(function (vol) {
Fdata.push({
value: vol.voltage,
date: new Date(vol.time),
});
});
if (this._isMounted) {
this.setState({
Fdata: Fdata,
});
}
})
}, 5000);
}
my issue here is when i initially select a device from picker, the line chart shows up perfect and reloads according to my set interval.
But when i select a different device from the picker, the line chart started to toggle between first and second device data and same continues with 3 rd selection and so on.
I guess i am not handling the state properly.
Any help will be appreciated. Thanks a ton.
You need to clean up the interval. Right now, you just keep creating additional intervals that will ping the device. One thing you could do is create a useEffect that will clear the interval after the selected device changes. The example below uses a placeholder, but you should be able to see the general idea.
const {useState, useEffect, Fragment} = React;
const Chart = ({deviceData}) => {
return (
<div>
{deviceData ? (
<Fragment>
<div>Date fetched: {Date.now()}</div>
<pre><code>{JSON.stringify(deviceData, null, 4)}</code></pre>
</Fragment>
) : (
<p>Waiting for device</p>
)}
</div>
);
};
const App = () => {
const [selectedDevice, setSelectedDevice] = useState("");
const [deviceAttrs, setDeviceAttrs] = useState();
useEffect(() => {
if (!selectedDevice) return;
const interval = setInterval(() => {
fetch(`http://jsonplaceholder.typicode.com/todos/${selectedDevice}`)
.then(res => res.json())
.then(data => setDeviceAttrs(data));
}, 5000);
return () => clearInterval(interval);
}, [selectedDevice]);
const options = [1, 2, 3];
return (
<div>
<select onChange={e => setSelectedDevice(e.target.value)}>
<option value="">Please select a device</option>
{options.map(option => (
<option key={option} value={option}>Device {option}</option>
))}
</select>
{selectedDevice ? (
<Chart deviceData={deviceAttrs} />
) : (
<div>Please select a device</div>
)}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="app"></div>