React: props not passing correct in React - javascript

I was trying to pass response message back from server and displayed it in the UI, but seems whenever the response I passed in the second <Dialog>, the response is undefined, I think response is only defined in the handle submit function.
Is there a way that I can pass the response into the <Dialog>?
const AppNav = () => {
//...
const [popupOpen, setPopupOpen] = useState(false);
const [secondPopupOpen, setSecondPopupOpen] = useState(false);
const [input, setInput] = useState('');
const renderContent = () => {
return (
<form>
<input
type="text"
onChange={e => setInput(e.target.value)}
value={input}
onBlur={() =>delay(() =>setInput(''),150}
placeholder='placeholder here...'
/>
</form>
);
};
const renderAction = () => {
const handleSubmit = async() => {
//some server calls here
//response is what got back from server call
const response = await service.send(request)
try{
if(response.result ==='success'){
setPopupOpen(false);
setSecondPopupOpen(true);
}else { throw new Error()}
}
catch (err) {throw new Error()}
return response;
}
return (
<Button onClick={handleSubmit}>Submit</Button>
);
};
//...
return (
<Button onClick={() => setPopupOpen(true)}>open button</Button>
<Dialog
open={popupOpen}
renderContent={renderContent}
renderAction={renderAction}
/>
<Dialog
open={secondPopupOpen}
renderContent={(response) => <span><>{response.resultMessage}</span>}
renderAction={() => {}}
/>
)
}
Dialog.js
const Dialog = props => {
const { renderAction, renderContent, renderTitle} = props;
return (
render={
({getTitleProps, getContentProps, getActionProps}) => (
<DialogTitle {...getTitleProps()}>
{renderTitle ? renderTitle() : <span>Title</span>}
</DialogTitle>
<DialogContent {...getContentProps()}>
{renderContent ? renderContent() : <span>Content</span>}
</DialogContent>
<DialogAction {...getActionProps()}>
{renderAction ? renderAction() : <span>Action</span>}
</DialogAction>
)
}
)
}

I think you should refactor renderAction function and store the state of request somewhere as well as error ( or combine them into one state ).
const [response, setResponse] = useState(null);
const [error, setError] = useState(null);
const handleSubmit = async() => {
const response = await service.send(request)
try {
if(response.result ==='success'){
setPopupOpen(false);
setSecondPopupOpen(true);
setResponse(response);
return;
}
setError(new Error('error'));
}
catch (error) {
setError(error);
}
}
const renderAction = () => {
if(response) {
return response;
}
if(error) {
return error.message // to-do
}
return <Button onClick={handleSubmit}>Submit</Button>
};

Related

Reload updated Table once a column is removed

this is a component that retrieve data from API and generate a table. One button in a column for removing the row opens a Modal.
For avoiding in first component render to trigger api.delete request, an useRef set as false in second useEffect
Modal's Delete button return row info in deleteHandler function which successfully trigger api.delete request on confirmation, remove user in backend however table is not reloading.
Once row is removed expected result is triggering api.get request and display table now updated and without row removed.
In order to get that result I tried with const [reload, setReload] = useState(false); state which introduce another dependency to both userEffect
reload state effectively reload table data updated however it cause also that api.delete request trigger with const ida undefined. Below component script it can find useEffect with my tried.
Any hint or possible solution is appreciated
import React, { Fragment, useEffect, useState, useRef } from "react";
... other imports ...
export default function AllUsers() {
const api = useApi();
const [userData, setUserData] = useState();
const [showModal, setShowModal] = useState(false);
const navigate = useNavigate();
const [modalMessage, setModalMessage] = useState();
const [removeUserId, setRemoveUserId] = useState();
const [ida, setIda] = useState();
let effectRan = useRef(false);
const [reload, setReload] = useState(false);
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida]);
const handleEditClick = (e, rowIndex) => {
// console.log("user/" + rowIndex.username);
navigate("/user/" + rowIndex.username, [navigate]);
};
const handleRemoveClick = (e, rowIndex) => {
// console.log([rowIndex]);
setShowModal(true);
setModalMessage({
title: `Remove ${rowIndex.username}`,
message: `User's email to remove ${rowIndex.email}`,
});
setRemoveUserId(rowIndex.id);
};
const closeHandler = () => {
setShowModal(false);
};
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setShowModal(false);
};
// console.log(ida, idb);
return (
<Fragment>
{showModal && (
<BtModal
show={showModal}
title={modalMessage.title}
message={modalMessage.message}
handleClose={closeHandler}
onConfirm={deleteHandler}
/>
)}
<Body>
<h1>User Table</h1>
{userData === undefined ? (
<Spinner animation="border" />
) : (
<>
{userData === null ? (
<p>Could not retrieve users.</p>
) : userData.length === 0 ? (
<p>There are not users in system</p>
) : (
<UserListTable
newData={userData}
handleEditClick={handleEditClick}
handleRemoveClick={handleRemoveClick}
/>
)}
</>
)}
</Body>
</Fragment>
);
}
useEffect updated with reload state:
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
effectRan.current = false;
} else {
setUserData(null);
}
})();
}, [api, reload]);
useEffect(() => {
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => (effectRan.current = true);
}, [api, ida, reload]);
Here you modify your last edit
const deleteHandler = () => {
// console.log(removeUserId);
setIda(removeUserId);
setReload(!reload)
setShowModal(false);
};
useEffect(() => {
(async () => {
const response = await api.get("/admin/users");
if (response.ok) {
setUserData(response.body);
} else {
setUserData(null);
}
})();
}, [api]);
useEffect(() => {
effectRan.current = reload;
if (effectRan.current) {
// console.log("effect run");
(async () => {
const response = await api.delete("/admin/users", {
ida: ida,
});
if (response.ok && response.status === 204) {
console.log(response);
setReload(!reload);
} else {
console.log(response.body.errors);
}
})();
}
return () => {
effectRan.current = false;
};
}, [api, ida, reload]);
It looks like you're trying to delete an item in the table and confirm it via a prompt. I can see that you've used a lot of useEffects, which might be making things a bit complicated.
Don't worry though, I have a solution that should be much simpler. Let me show you what I mean!
const Page = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState([]);
const [deleting, setDeleting] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
const deleteHandler = async() => {
setDeleting(true);
await api.delete(selectedItem);
// Next you can either update the state itself
let tD = [...data];
const index = tD.findIndex((i) => i.id == selectedItem);
tD.splice(index, 1);
setSelectedItem(tD);
// or refetch data from the backend and update the state
const d = await api.getData();
setData(d);
setDeleting(false);
setSelectedItem(null);
};
useEffect(() => {
const fetchData = async() => {
setLoading(true);
const d = await api.getData();
setData(d);
setLoading(false);
}
fetchData();
}, [])
return loading ? <div>Loading...</div> : <>
{/*Other JSX elements*/}
{selectedItem && <div>
{/*prompt body*/}
<button onClick={() => {setSelectedItem(null)}}>Cancel</button>
<button disabled={deleting} onClick={() => {deleteHandler()}}>Delete</button>
{/*Button will be disabled when the DELETE request is sent*/}
</div>}
{data.map((row) => {
return <tr key={row.id}>
<td>...</td>
<td>...</td>
<td>
<button onClick={setSelectedItem(row.id)}>Delete</button>
</td>
</tr>
})}
</>
}

How to implement multiple functions on a single event handler?

I have this simple react native app that on button click it redirects user to a page. I'm saving data in a cache so that if user clicks the store button it stores data and after refresh it sets store data. However, I would like to implement this logic on a single button, not on 2 different buttons as it is now. Can someone explain me how could I achieve this?
export const App = () => {
const [showFirstWeb, setFirstWeb] = useState(false);
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem("#web_Key");
return setFirstWeb(JSON.parse(jsonValue));
} catch (e) {
console.log("error", e);
}
};
useEffect(() => getData, []);
const storeData = async () => {
try {
const jsonValue = JSON.stringify(showFirstWeb);
await AsyncStorage.setItem("#web_Key", jsonValue);
} catch (e) {
console.log("error", e);
}
};
return (
<View>
<View style={styles.buttonStyle}>
<Button onPress={setFirstWeb(!showFirstWeb)}/>
<Button onPress={storeData} title="store"/>
<View>
{showFirstWeb && <MyWebComponent uri="https://www.google.com/" />}
</View>
</View>
);
};
const MyWebComponent = (uri) => {
return <WebView source={uri} />;
};```
export const App = () => {
const [showFirstWeb, setFirstWeb] = useState(false);
const getData = async () => {
try {
const jsonValue = await AsyncStorage.getItem("#web_Key");
return setFirstWeb(JSON.parse(jsonValue));
} catch (e) {
console.log("error", e);
}
};
// you forgot to call the function here
useEffect(() => getData(), []);
const storeData = async () => {
try {
// get the new value
const newShowFirstWeb = !showFirstWeb
// use the new value
setFirstWeb(newShowFirstWeb)
const jsonValue = JSON.stringify(newShowFirstWeb );
await AsyncStorage.setItem("#web_Key", jsonValue);
} catch (e) {
console.log("error", e);
}
};
return (
<View>
<View style={styles.buttonStyle}>
<Button onPress={storeData} title="store"/>
<View>
{showFirstWeb && <MyWebComponent uri="https://www.google.com/" />}
</View>
</View>
);
};

How can i turn this React class component into a functional component?

I'm working on implementing a braintree payment method in my react/mui app. I've found a way that works, but it's in a class component. How can I convert this info a proper functional component?
const BraintreeDropInPaymentMethod = () => {
class Store extends React.Component {
instance;
state = {
clientToken: '<BRAIN TREE KEY>'
};
async componentDidMount() {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
this.setState({
clientToken,
});
}
async buy() {
const { nonce } = await this.instance.requestPaymentMethod();
await fetch(`server.test/purchase/${nonce}`);
}
render() {
if (!this.state.clientToken) {
return (
<div>
<h1>Loading...</h1>
</div>
);
} else {
return (
<div>
<DropIn
options={{ authorization: this.state.clientToken }}
onInstance={(instance) => (this.instance = instance)}
/>
<Button
variant='contained'
onClick={this.buy.bind(this)}
>
Create Account
</Button>
<Button
variant='outlined'
sx={{ marginLeft: 3 }}
color='warning'
onClick={(e) => handleCancelAccountCreation(e)}
href='/store-front'
>
Cancel
</Button>
</div>
);
}
}
}
const [user, setUser] = useState({})
const handleCancelAccountCreation = (event) => {
setUser({})
document.getElementById('signInBtn').hidden = false
}
return (
<Store/>
)
}
this is my attempt, but I'm coming up short on how I should handle componentDidMount(). I know how to handle useState in some situations, except for this one. Also, how can I handle the 'instance' section in a functional format? thanks.
const BraintreeDropInPaymentMethod = () => {
const [token, setToken] = useState('<BRAIN TREE KEY>')
const [user, setUser] = useState({})
const contactServer = async () => {
const res = await fetch('server.test/client_token')
const clientToken = await res.json()
console.log(clientToken)
setToken(token)
}
const buy = async () => {
const { nonce } = await this.instance.requestPaymentMethod()
await fetch(`server.test/purchase/${nonce}`)
}
const handleCancelAccountCreation = (event) => {
setUser({})
document.getElementById('signInBtn').hidden = false
}
const createAccountOptions = () => {
if (!token) {
return (
<div>
<h1>Loading...</h1>
</div>
) else {
return (
<div>
<DropIn
options={ authorization: {setToken})
onInstance={(instance) => (this.instance = instance)}
/>
<Button
variant="contained'
onClick={buy}
>
Create Account
</Button
variant='outlined'
sx={{ marginLeft: 3 }}
color='warning'
onClick={(e) => handleCancelAccountCreation(e)}
href='/store-front'
>
<Button>
Cancel
</Button>
</div>
)
}
}
}
return(
<>
<createAccountOptions/>
</>
)
}
The functional equivalent of componentDidMount() is the useEffect hook.
In this case you would change this:
async componentDidMount() {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
this.setState({
clientToken,
});
}
Into something like this:
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch("server.test/client_token");
const clientToken = await response.json();
setState((old) => clientToken);
};
Using the useEffect hook with an empty array as a dependency makes the function in it only run once as the component mounts.

Searchbar cant filter JSON File Javascript

today i have a problem with my searchbar.
const [posts, setPosts] = useState(null)
const [searchTerm, setSearchTerm] = useState("")
useEffect(() => {
const loadPosts = async () => {
try {
const post = await getAllPosts()
setPosts(post)
} catch (e) {
alert("Couldn't load posts")
}
}
loadPosts()
}, [])
return (
<div>
<input type={"text"} placeholder="Search..." onChange={event => {
setSearchTerm(event.target.value)
}}/>
</div>
)
}
This is my Searchbar Component. In the Index file, did i gave a props with.
const [posts, setPosts] = useState([])
const [searchTerm, setSearchTerm] = useState("")
useEffect(() => {
const loadPosts = async () => {
try {
const post = await getAllPosts()
setPosts(post)
} catch (e) {
alert("Couldn't load posts")
}
}
loadPosts()
}, [])
return (
<div className={styles.posts}>
<h1>Market-place Valando</h1>
<SearchList title={posts.filter(post => {
if (post.title.toLowerCase().includes(searchTerm.trim().toLowerCase()) && searchTerm.trim() !== "") {
return post.title
}
}).map(titles => {
{
{titles.title}
}
}
)}/>
{
posts.map(post => {
return (
<div key={post.id} className={styles.key}>
<h1>{post.title}</h1>
<Image width={1000} height={1000} src={post.image}/>
<p>Price: {post.price}.-</p>
<p>Description: {post.description}</p>
<Link href={`/posts/${post.id}`} passHref>
<a>Read more</a>
</Link>
</div>
)
})
}
</div>
)
}
I have a db.json file that i connected with an API File. In this Api File i made all the fetch stuff. This shouldnt be the problem. I think the problem is, that the filter doesnt work properly, with the titels.
You are correct, JavaScript filter does not return specific property values, but it returns the top entries of the array, a.k.a posts. So return post.title or return true will yield the same result. However, the problem in your code appears to be that you are not returning anything from the map function. All you need to do is to change it to the following:
.map(post => post.title)

React Suspense not working as expected when fetching data

I have a react component in which I am fetching data from the pokeapi. I am trying to render it with Suspense using browser's fetch but I am not able to figure out where I am going wrong.
function FirstPageIndex() {
const [pokemon, setPokemon] = useState({ abilities: [] });
const [loading, setLoading] = useState(false);
console.log("This has been rendered only once");
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const getDataFunction = async (e) => {
try {
e.preventDefault();
setLoading(true);
const r = await fetch("https://pokeapi.co/api/v2/pokemon/ditto");
const res = await r.json();
await sleep(4000);
console.log("This is the data");
console.log(res);
setPokemon(res);
setLoading(false);
} catch (e) {}
};
return (
<div>
This is coming from the home page
<button onClick={getDataFunction}>Get Data</button>
<Suspense fallback={<h2>Suspense in loading...</h2>}>
{pokemon.abilities.map((ability, i) => (
<p key={i}>
This is my ability {i + 1}: {ability.ability.name}
</p>
))}
</Suspense>
</div>
);
}
I have imported {Suspense} from "react" in this component.
The expected behaviour is when I click on get data, it waits for 3 seconds and then it renders the data, during which time it should show the Suspense's fallback which it is not showing.
EDIT: Updated code to incorporate promise based delay
Please have a look at it?
setTimeout is not a promise so await won't work here.
try doing this
function FirstPageIndex() {
const [pokemon, setPokemon] = useState({ abilities: [] });
const [loading, setLoading] = useState(false);
useEffect(() => {}, []);
console.log("This has been rendered only once");
const getDataFunction = async (e) => {
try {
e.preventDefault();
setLoading(true);
const r = await fetch("https://pokeapi.co/api/v2/pokemon/ditto");
const res = await r.json();
setTimeout(() => {
console.log("This is the data");
console.log(res);
setPokemon(res);
setLoading(false);
}, 3000);
} catch (e) {}
};
if (loading) return <p>This is loading state</p>;
return (
<div>
This is coming from the home page
<button onClick={getDataFunction}>Get Data</button>
<Suspense fallback={<h2>Suspense in loading...</h2>}>
{/* <PokeData resource={pokemon} ></PokeData> */}
{pokemon.abilities.map((ability, i) => (
<p key={i}>
This is my ability {i + 1}: {ability.ability.name}
</p>
))}
</Suspense>
</div>
);
}

Categories