How to call function in a different component in React.js? - reactjs

I have two components.
DropDownForRoomChangeCondo.js js that displays radio buttons. DiscoverCondoRoom.js displays DropDownForRoomChangeCondo.js.
What I want to achieve is In DropDownForRoomChangeCondo.js, When sending a request to the backend with handleChange (when switching the radio button) I want to change the screen display by calling getDevices(); in DiscoverCondoRoom.js. (Since the assignment of the room where the device is installed changes when the radio button is switched, I want to update the display)
Issue/error message
Currently, when sending a request to the backend with handleChange (when switching the radio button) Display update does not occur.
DropDownForRoomChangeCondo.js
import Dropdown from 'react-bootstrap/Dropdown';
const DropDownForRoomChangeCondo = (item) => {
const history = useHistory();
const [devices, setDevices] = useState([]);
const handleChange = e => {
setVal(e.target.name);
setDeviceRoomName(e.target.name);
}
const setDeviceRoomName = async(data) => {
console.log("Body sent to server", {
attributes:
[
{
entity_id : item.item.entity_id,
room_name: data
}
]
})
await axios.post('xxx.com',
{
attributes:
[
{
entity_id : item.item.entity_id,
room_name: data
}
]
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
console.log('Set Device Room Name!');
getDevices();
})
.catch(err => {
console.log(err);
console.log('Missed Set Device Room Name!');
});
}
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)
console.log("bbbbbbbbbbb")
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(item.item.room_name);
console.log(val)
console.log(typeof(val))
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
<img className="ic_edit" src={ic_edit} />
</a>
));
useEffect(() => {
getDevices();
},[]);
return (
<>
<div className="">
<p>{item.item.room_name}</p>
<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}
name={room_names}
onChange={handleChange}
checked={val === room_names}
/>
<p className="drop_down_p">{room_names}</p>
</div>
))}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</div>
</>
);
}
export default DropDownForRoomChangeCondo;
DiscoverCondoRoom.js
const DiscoverCondoRoom = () => {
const history = useHistory();
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 => {
setDevices(result.data.attributes);
})
.catch(err => {
console.log(err);
});
}
useEffect(() => {
getDevices();
},[]);
const lll = Object.keys(devices);
const object_device_value = Object.values(devices).flat();
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))];
return (
<>
<div className="container condo_container">
{entities.map((entity, i) => (
<DropDownForRoomChangeCondo item={entity} />
))}
</div>
</>
);
}
};
export default DiscoverCondoRoom;

You need to pass your getDevices Method as a prop to your dropdown component.
<DropDownForRoomChangeCondo item={entity} getDevices={getDevices} />
Then inside your DropDown Component, call the getDevices Method at your desired place by calling props.getDevices().
Also, i would suggest to define props like so:
const DropDownForRoomChangeCondo = (props) => {
const history = useHistory();
const [devices, setDevices] = useState([]);
…
And then access item by pointing to props.item

Related

Tree menu by consuming API and Params

I have a question and at the same time ask you for help because I have a problem in my coding.
So, I intend to display the "City" menu based on the "Province" parameter in my menu component tree.
I previously managed to display the provincial name menu in the tree menu component, the result is like this.
Well, later when the menu names of the provinces will appear the names of the cities based on the "province" parameter. However, when I tried it, it failed. The result is like this
Here's my code =
Building.js
export const getBuildingOLDallProvinsi = () => {
return new Promise((resolve, reject) => {
axios
.get(`${baseUrl}/api/nad/buildingCount`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
if (error.response?.data.code === 404)
resolve({ lists: [], totalCount: 0 });
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
export const getBuildingOLDallKota = (provinsi) => {
return new Promise((resolve, reject) => {
axios
.get(`${baseUrl}/api/nad/buildingCount/${provinsi}`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => {
resolve(response.data.data);
})
.catch((error) => {
if (error.response?.data.code === 404)
resolve({ lists: [], totalCount: 0 });
console.log(error.response);
reject(error?.response?.data?.message || "Network error.");
});
});
};
TreeData.jsx
import React, { useEffect, useState } from "react";
import TreeMenu from "react-simple-tree-menu";
import "react-simple-tree-menu/dist/main.css";
import {
getBuildingOLDallProvinsi,
getBuildingOLDallKota,
} from "../../../service/building";
import { useParams } from "react-router-dom";
const TreeData = () => {
const [countData, setCount] = useState([]);
const [countData2, setCount2] = useState([]);
const getDataAllProvinsi = () => {
getBuildingOLDallProvinsi()
.then((resolve) => {
console.log(resolve);
setCount(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
const { provinsi } = useParams();
const getDataAllKota = (param) => {
getBuildingOLDallKota({ ...param, provinsi: provinsi })
.then((resolve) => {
console.log(resolve);
setCount2(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
useEffect(() => {
getDataAllProvinsi();
getDataAllKota();
}, []);
return (
<>
<div className="row">
<div className="col text-center">
<p className="mt-3">
<div className="row mt-3 d-flex justify-content-center cursor-pointer">
<div className="col-lg-8 text-left text-dark">
<TreeMenu
cacheSearch
data={[
{
key: "provinsi",
label: "Provinsi",
nodes: countData.map((data) => {
return {
key: data.id,
label: [data.provinsi, data.total_building],
nodes: [
{
key: "kota",
label: "Kota",
nodes: countData2.map((data) => {
return {
key: data.provinsi,
label: [data.kota, data.total_building],
nodes: [
{
key: data.id,
label: data.total_building,
nodes: [],
},
],
};
}),
},
],
};
}),
},
]}
debounceTime={125}
disableKeyboard={false}
hasSearch={false}
onClickItem={function noRefCheck() {}}
resetOpenNodesOnDataUpdate={false}
/>
</div>
</div>
</p>
</div>
</div>
</>
);
};
export default TreeData;
Thank you in advance, your help is very helpful for me and I really respect all of your answers.
Edit = this is my response API
pastebin(dot)com/Bua3FThZ
pastebin(dot)com/ERSCHDSR
Update:
In the console I can display param data based on the province.
For example, the data for the province is "ACEH", then the ACEH data appears in the console ...
const getDataAllKota = (param) => {
getBuildingOLDallKota("ACEH")
.then((resolve) => {
console.log(resolve);
setCount2(resolve);
})
.catch((reject) => {
console.log(reject);
});
};
console.log(countData2);
Now I'm confused about how to display param data by province in the component tree menu.

Change the state when clicking on a button with react

I'm trying to send and see my data status in my console log, when I click on 'Cancel' button, the status will be change by status:cancel, if I click on 'finish' button then the status is status:finish and same idea for the last one with save. Here what I've try to do but the status is not working
export default function App() {
const [data, setData] = useState({
status: ""
});
const [status, setStatus] = useState("");
const saveState = () => {
setStatus("saved");
};
const finishState = () => {
setStatus("finish");
};
const pendingState = () => {
setStatus("pending");
};
useEffect(() => {
axios
.post("")
.then((res) => {
console.log(res);
setInvitations(res.data.invitations[0]);
})
.catch((err) => {
console.log(err);
});
}, []);
function submit(e) {
e.preventDefault();
axios
.post("", {
status: data.status
})
.then((res) => {
console.log(res.data);
});
}
return (
<>
<form onSubmit={(e) => submit(e)}>
<button onClick={saveState}>Save</button>
<button onClick={finishState}> Finish</button>
<button onClick={pendingState}> Cancel</button>
</form>
</>
);
}
you can use simple setsate
export default function App() {
const [data, setData] = useState({
status: "",
});
const [status, setStatus] = useState("");
useEffect(() => {
axios
.post("")
.then((res) => {
console.log(res);
setInvitations(res.data.invitations[0]);
})
.catch((err) => {
console.log(err);
});
}, []);
function submit(e) {
e.preventDefault();
axios
.post("", {
status: data.status,
})
.then((res) => {
console.log(res.data);
});
}
return (
<>
<form onSubmit={(e) => submit(e)}>
<button onClick={() => setStatus({ status: "saved" })}>Save</button>
<button onClick={() => setStatus({ status: "finish" })}> Finish</button>
<button onClick={() => setStatus({ status: "pending" })}>
{" "}
Cancel
</button>
</form>
</>
);
}
You are using setStatus to change the status, but you are using axios.post() on your data.status
You need to either setData in your 3 functions
const saveState = () => {
setData({status:"saved"});
};
const finishState = () => {
setData({status:"finish"});
};
const pendingState = () => {
setData({status:"pending"});
};
or you can change axios.post to:
function submit(e) {
e.preventDefault();
axios
.post("", {
status: status //This is the change
})
.then((res) => {
console.log(res.data);
});
}

Live pagination on search bar with hooks in reactjs

Hello guys i am trying to implement the live pagination of searched items. I have done only the pagination for all the products but not for the searching ones and i am having some problems into writting it.
Thank you for your time.
This is app.js:
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [currentData, setCurrentData] = useState([]);
const [columns, setColumns] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [dataPerPage, setDataPerPage] = useState(10);
const [searchTerm, setSearchTerm] = useState("");
const [filteredProduct, setFilteredProducts] = useState("");
useEffect(() => {
var myHeaders = new Headers();
myHeaders.append("Accept", "text/plain");
myHeaders.append(
"Authorization",
"Bearer xxx"
);
var requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow",
};
fetch("http://localhost:5000/api/WarehousStock", requestOptions)
.then((response) => response.json())
.then((result) => {
setData(result);
})
.catch((error) => console.log("error", error))
.finally(() => setLoading(false));
}, []);
// this will run evertime one of the following state will change => data, currentPage, dataPerPage
useEffect(() => {
// generate dynamically columns from first object from array
setColumns(
Object.keys(data[0] || []).map((key) => ({
Header: key,
accessor: key,
}))
);
filterData();
}, [data, currentPage, dataPerPage]);
var paginate = (pageNumber) => setCurrentPage(pageNumber);
function filterData() {
const indexOfLastData = currentPage * dataPerPage;
const indexOfFirstData = indexOfLastData - dataPerPage;
// if there is a search term
if (searchTerm !== '') {
let result =
data.filter(data => {
return data.articleName.toLowerCase().includes(searchTerm.toLowerCase())
})
result = result.slice(indexOfFirstData, indexOfLastData)
setCurrentData([...result])
} else {
// if there is no a search term
setCurrentData(data.slice(indexOfFirstData, indexOfLastData));
}
}
useEffect(() => {
filterData();
}, [searchTerm])
if (loading) return <p>Loading...</p>;
return (
<Styles>
<div className="SearchButton"><input type="text" placeholder="Search name of product" onChange={event => { setSearchTerm(event.target.value) }} /></div>
<Table columns={columns} data={currentData} />
<Pagination
dataPerPage={dataPerPage}
totalData={data.length}
paginate={paginate}
/>
</Styles>
);
}
This is the pagination component:
const Pagination = ({ dataPerPage, totalData, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalData / dataPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<ul className="pagination mt-4">
{pageNumbers.map((number) => (
<li key={number} className="page-item">
<a onClick={() => paginate(number)} href="!#" className="page-link">
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
Thank you for your help!

Trying to create a fetch request with react hooks, my data is still null

Relatively new to using hooks, but I create this useFetch hook and I am trying to sign in a user after getting their information from text fields, I am able to create a successful request by hard coding {email:someemail, password: somepassword} but the issue arises when I am trying to grab the input from my state.
The error states I am missing a parameter
Here is my component:
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// #ts-ignore
const postData =useData({email:email, password:password})
const thisWorks = useData({email:"someEmail", password:"somePassword"})
return (
<>
{console.log(thisWorks)}
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={() => postData}
text="Login"
/>
</>
)
}
And here is my query:
const useData = (data: loginInfo): QueryType[] | string => {
const fetched = useFetch<Query>(
"example.com",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
);
if (fetched.status === "loading") {
return fetched.status;
} else if (fetched.status === "loaded") {
const { payload } = fetched;
// #ts-ignore
return payload;
} else if (fetched.status === "error") {
return fetched.status;
}
return "Error";
};
my useFetch Hook:
const useFetch = <T>(url: string, headers?: Header | any) => {
const isCurrent = useRef(true);
const [result, setResult] = useState<Service<T>>({
status: "loading",
});
useEffect(
() => () => {
// called when the component is going to unmount
isCurrent.current = false;
},
[]
);
useEffect(() => {
fetch(url, headers)
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return result;
};
I'm curious to ask,
will you send many API requests while you typing email and password?
<Button
onClick={() => postData}
text="Login"
/>
this code doesn't seem to work, postData is not a function, just an object
may be you can modify your code like this (I'm not tested just sample code)
your component
const Login: FunctionComponent = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const login = useFetch('http://path/to/login', { method: 'POST' })
React.useEffect(() => {
if (login.status === 'loaded') {
// do your stuff
}
}, [login.status])
const handleLogin = () => {
login.setPayload({ email, password })
}
if (login.status === 'loading') {
return <div>Loading...</div>
}
if (login.status === 'error') {
return <div>{login.error}</div>
}
return (
<>
<TextFieldComponent
isRequired={true}
label={'Email'}
value={email}
// #ts-ignore
handleChange={(e)=> setEmail(e.target.value) }
/>
<TextFieldComponent
isRequired={true}
label={'Password'}
value={password}
// #ts-ignore
handleChange={(e)=> setPassword(e.target.value) }
/>
<Button
onClick={handleLogin}
text="Login"
/>
</>
)
}
useFetch
const useFetch = <T>(url: string, options: RequestInit) => {
const [payload, setPayload] = React.useState({})
const [start, setStart] = React.useEffect(false)
useEffect(() => {
if (!start) {
return
}
fetch(url, {
...options,
headers: {
"Content-Type": "application/json",
...options.headers
},
body: JSON.stringify(payload),
})
.then((resp) => resp.json())
.then((response) => setResult({ status: "loaded", payload: response }))
.catch((error) => setResult({ status: "error", error }))
.finally(() => setStart(false))
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url, start, payload, options]);
return {
...result,
setPayload: (payload) => {
setPayload(payload)
setStart(true)
}
}
}
This can be written using the useEffect hook
const useFetch = (url, options) => {
const [response, setResponse] = React.useState(null);
const [status, setStatus] = React.useState("loading");
useEffect(async () => {
try {
const res = await fetch(url, options);
const json = await res.json();
setResponse(json);
setStatus("success");
} catch (error) {
setStatus("error");
}
});
return [status, response];
};

Parent component rerender useEffect

I have a parent Component named Posts that has a useEffect and fetches data, then I have a child component named Post which renders the post from props. I have a delete and like function inside Post and I want the parent to rerender when post get liked or post gets deleted. How can I achieve this?
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
getPosts();
}, []);
const getPosts = async () => {
const data = await fetch('https://localhost:1111/api/posts/posts', {
credentials: 'include'
});
const response = await data.json();
console.log(response);
setPosts(response);
};
return (
<div>
{posts.map(post => (
<Post key={post.id} post={post} />
))}
</div>
);
};
const Post = props => {
const like = async post => {
const formData = new FormData();
formData.append('id', post);
const data = await fetch('https://localhost:1111/api/posts/like', {
method: 'POST',
body: formData,
credentials: 'include'
});
const response = await data.json();
console.log(response);
};
const deletePost = async post => {
const formData = new FormData();
formData.append('id', post);
const data = await fetch('https://localhost:1111/api/posts/deletepost', {
method: 'POST',
body: formData,
credentials: 'include'
});
const response = await data.json();
console.log(response);
};
return (
<div>
<img
src={`http://localhost:1111/api/posts/uploads/images/${props.post.content}`}
alt={`Post ${props.post.id}`}
/>
<button onClick={() => like(props.post.id)}>Like</button>
<button onClick={() => deletePost(props.post.id)}>Delete</button>
</div>
);
};
I would recommend you to have an additional prop method (onUpdate) on the child <Post /> component, which gets triggered whenever the deletePost() or like() are triggered.
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
getPosts();
}, []);
const getPosts = async () => {
const data = await fetch('https://localhost:1111/api/posts/posts', {
credentials: 'include'
});
const response = await data.json();
console.log(response);
setPosts(response);
};
const handleUpdate = () => {
getPosts();
}
return (
<div>
{posts.map(post => (
<Post key={post.id} onUpdate={() => handleUpdate()} post={post} />
))}
</div>
);
};
const Post = props => {
const { onUpdate } = props;
const like = async post => {
const formData = new FormData();
formData.append('id', post);
const data = await fetch('https://localhost:1111/api/posts/like', {
method: 'POST',
body: formData,
credentials: 'include'
});
const response = await data.json();
console.log(response);
onUpdate();
};
const deletePost = async post => {
const formData = new FormData();
formData.append('id', post);
const data = await fetch('https://localhost:1111/api/posts/deletepost', {
method: 'POST',
body: formData,
credentials: 'include'
});
const response = await data.json();
console.log(response);
onUpdate();
};
return (
<div>
<img
src={`http://localhost:1111/api/posts/uploads/images/${props.post.content}`}
alt={`Post ${props.post.id}`}
/>
<button onClick={() => like(props.post.id)}>Like</button>
<button onClick={() => deletePost(props.post.id)}>Delete</button>
</div>
);
};
As you can see, onUpdate() is called at the end of both deletePost() and like(), and on the parent Posts component, handleUpdate() will be called, which in turn submits the fetch response to update the posts state. This will cause the parent <Posts /> to re-render.
Keep post functionality inside the parent component and pass the update functions to Post
const Posts = () => {
const [posts, setPosts] = useState([])
useEffect(() => {
fetch('https://localhost:1111/api/posts/posts', {
credentials: 'include',
})
.then(res => res.json())
.then(setPosts)
}, [])
const likePost = async postId => {
await fetch...
setPosts(prevState =>
prevState.map(post =>
post.id === postId
? {
...post,
likes: post.likes + 1,
}
: post
)
)
}
const deletePost = async postId => {
await fetch...
setPosts(prevState => prevState.filter(post => post.id !== postId))
}
return (
<div>
{posts.map(post => (
<Post
key={post.id}
post={post}
likePost={likePost}
deletePost={deletePost}
/>
))}
</div>
)
}
const Post = ({ post, likePost, deletePost }) => (
<div>
<img
src={`http://localhost:1111/api/posts/uploads/images/${post.content}`}
alt={`Post ${post.id}`}
/>
<button onClick={() => likePost(post.id)}>Like</button>
<button onClick={() => deletePost(post.id)}>Delete</button>
</div>
)

Resources