How do I get it to exit loading using useLazyQuery? - reactjs

First of all, hello everyone. I'm trying to improve myself with Graphql. Normally, I can display the GET_CHARACTERS variable that I define by calling useQuery. There is no problem here. But I want to display the data that will correspond to the value I entered in the input field. I used useLazyQuery for this. But here is the problem. I enter the text and then click the button. It stays on Loading all the time. It doesn't switch to display. I've also looked at other articles. I've also applied all the fetchPoliciys in useLazyQuery, but the result hasn't changed at all. I also tried notifyOnNetworkStatusChange: true, and it didn't work either.
I would appreciate your help. Thanks in advance.
import React, { useEffect, useState } from "react";
import { useQuery, gql, useLazyQuery } from "#apollo/client";
import Card from "./components/Card";
const GET_CHARACTERS = gql`
query GetCharacterLocations($name: String!) {
characters(filter: { name: $name }) {
results {
id
name
image
location {
name
}
}
}
}
`;
const App=()=> {
const [name, setName] = useState("");
const [getRickMorty, { loading, error, data, called }] = useLazyQuery(
GET_CHARACTERS,
{
variables: { name },
}
);
console.log({ data, loading, error });
return (
<div className="container">
<input
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button
onClick={() => {
getRickMorty();
}}
>Search
</button>
{loading && <div>Loading</div>}
{error && <div>Error</div>}
{data?.characters?.results?.map((characters) => (
<Card character={characters} />
))}
</div>
);
}
export default App;

Related

Passing multiple form input fields' data in a child component to its parent in React (hooks)

I have a child component that isn't re-rendering because the state inside its parent isn't updating. I've recently found out that I need to pass data from child to parent, but I'm not sure how to do that. Most tutorials I've found on the subject show you how to pass one field or piece of information over to the parent by sending a function, but I have multiple fields on a form I need to send over to the parent component. I'm not sure how to go about that.
Here's the parent component.
import React, { useState } from "react";
import { useQuery } from "#apollo/client";
import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
const StudentList = () => {
const [student, setStudent] = useState("");
const { loading, error, data } = useQuery(getStudents);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
const handleClick = (student)=> {
//console.log(student)
setStudent(student);
};
let filteredStudents = [];
//console.log(data.students)
for(let i = 0; i < data.students.length; i++){
//console.log(data.students[i].class.name)
if(data.students[i].class.name === "1FE1"){
//console.log(data.students[i].name)
filteredStudents.push(data.students[i])
}
}
//console.log(filteredStudents);
return (
<div>
<ul id="student-list">
{data.students.map((student) => (
<li key={student.id} onClick={(e) => handleClick(student)}>{student.name}</li>
))}
</ul>
{
student ? <StudentDetails student={student} />
: <p>No Student Selected</p>
}
</div>
);
};
export default StudentList;
And here is the child component called StudentDetails which displays a student's individual information that isn't re-rendering because StudentList's state isn't changing.
import React from "react";
import { useEffect, useState } from "react";
import { getStudentQuery } from "../queries";
import { useQuery } from "#apollo/client";
import DeleteStudent from "./DeleteStudent"
import EditStudent from "./EditStudent";
const StudentDetails = (props)=> {
console.log(props)
const [astudent, setStudent] = useState(props)
return (
<div id="student-details" >
<h2>Name: {props.student.name}</h2>
<h3>Age: {props.student.age}</h3>
<h3>Class: {props.student.class.name}</h3>
<h3>Test 1 Score: {props.student.test1}</h3>
<DeleteStudent id={props.student.id}/>
<EditStudent id={props.student.id} />
</div>
)
}
export default StudentDetails;
Inside StudentDetails is another child component called "EditStudent" which is where I need to somehow pass the information submitted in the form's fields over to StudentList.
import React, { useEffect, useState } from "react";
import { useMutation } from "#apollo/react-hooks";
//import { getStudents } from "../queries";
import StudentDetails from "./StudentDetails";
import { editStudentMutation, getStudentQuery, getStudents } from "../queries/index";
const EditStudent = (props) => {
console.log(props)
const [name, setName] = useState();
const [age, setAge] = useState();
const [test, setTest] = useState();
const [editStudent] = useMutation(editStudentMutation);
const astudent = props
return (
<form id="edit-student"
onSubmit={(e) => {
e.preventDefault();
editStudent({
variables: {
id: props.id,
name: name,
age: age,
test1: test
},
refetchQueries: [{ query: getStudents}]
});
}}>
<div className="field">
<label>Student Name:</label>
<input type="text"
value={name}
onChange={(e) => setName(e.target.value)}/>
</div>
<div className="field">
<label>Age:</label>
<input type="text"
value={age}
onChange={(e) => setAge(e.target.value)}/>
</div>
<div className="field">
<label>Test One:</label>
<input type="text"
value={test}
onChange={(e) => setTest(e.target.value)}/>
</div>
<button>submit</button>
</form>
)
}
export default EditStudent;
So yeah, I think I understand what I need to do but I don't know where to start on how to pass all the info from EditStudent over to StudentList. As I mentioned, all the tutorials on the subject show how to send one individual piece of information, but not several pieces. Could anyone suggest any pointers on how to achieve this?
I think what you are looking for is lifting a state up; essentially the parent passes a state to the child component and they both can access and change the state. For your case I would suggest passing multiple states down to the child.
Here is an example that does this: enter link description here

How to highlight a JSON file with a copy option in react application?

I want to highlight a JSON file with a copy option in a JSON for the child. Child component code as below.
The below code gives an inline code highlight below. I want to show it as a code block. and users can copy the JSON file.
import React, { useEffect } from "react";
import { Card } from "antd";
import Highlight from 'react-highlight'
const ResponsesDataView = React.memo((props) => {
return (
<>
<Card size="small">
<Highlight language="json">
{JSON.stringify(props.jsonData)}
</Highlight>
</Card>
</>
);
});
export default ResponsesDataView
Beautify the the json with JSON.stringify(jsonData, null, 4)
Use textarea to display the data with readOnly and value prop.
Use useRef to handle the highlighting and copying operations.
Your ResponsesDataView will look something like this
import { useRef, memo, useCallback } from "react";
import styles from "./Json.module.css";
const ResponsesDataView = memo((props) => {
const { data } = props;
const txtarea = useRef(null);
const copyToClipboard = useCallback(() => {
txtarea.current.select();
document.execCommand("copy");
}, [txtarea]);
const selectTextAreaValue = useCallback(() => {
txtarea.current.select();
}, [txtarea]);
return (
<div className={styles.areaContainer}>
<div className={styles.btnContainer}>
<button className={styles.copyBtn} onClick={selectTextAreaValue}>
Select All
</button>
<button className={styles.copyBtn} onClick={copyToClipboard}>
Copy
</button>
</div>
<textarea
className={styles.txtArea}
ref={txtarea}
readOnly
value={JSON.stringify(data ? data : {}, null, 4)}
></textarea>
</div>
);
});
export default ResponsesDataView;
Here is a working demo

Material-UI Autocomplete, React Hook Form - Changing InputValue in Material UI Autocomplete with Use State in an OnChange?

I've been customising a Material UI Autocomplete within a Controller from React Hook Form, as part of a much larger form, with some difficulty.
The dropdown lists suggestions drawn from the database (props.items, represented here as objects) and if the suggestion is not there, there's the option to add a new one in a separate form with a button from the dropdown. This 'secondComponent' is opened with conditional rendering.
As it gets passed to the second form, the data is stored in state (heldData) and then passed back into the form via React Hook Form's reset, here as reset(heldData).
This updates the value of the form perfectly, as I have an onChange event that sets the value according to what was passed in. React Hook Form handles that logic with the reset and gives the full object to the onChange.
However, I also want to set the InputValue so that the TextField is populated.
In order to create a dynamic button when there are no options ('Add ....(input)... as a guest'), I store what is typed into state as 'texts'. I thought that I could then use the OnChange event to use the same state to update the inputValue, as below. However, when I setTexts from the onChange, the change isn't reflected in the inputValue.
Perhaps this is because the useState is async and so it doesn't update the state, before something else prevents it altogether. If so, it's much simpler than the other code that I have included, but wasn't certain. I have excluded most of the form (over 500 lines of code) but have tried to keep any parts that may be appropriate. I hope that I have not deleted anything that would be relevant, but can update if necessary.
Apologies. This is my first question on Stack Overflow and I'm quite new to React (and coding) and the code's probably a mess. Thank you
**Form**
import React, { useState, useEffect} from "react";
import AutoCompleteSuggestion from "../general/form/AutoCompleteSuggestion";
import SecondComponent from './SecondComponent'
import { useForm } from "react-hook-form";
const items = {
id: 2,
name: "Mr Anderson"
}
const items2 = {
id: 4,
name: "Mr Frog"
}
const defaultValues = {
guest: 'null',
contact: 'null',
}
const AddBooking = () => {
const { handleSubmit, register, control, reset, getValues} = useForm({
defaultValues: defaultValues,
});
const [secondComponent, setSecondComponent] = useState(false);
const [heldData, setHeldData] = useState(null)
const openSecondComponent = (name) => {
setSecondComponent(true)
const data = getValues();
setHeldData(data);
}
useEffect(() => {
!secondComponent.open?
reset(heldData):''
}, [heldData]);
const onSubmit = (data) => {
console.log(data)
};
return (
<>
{!secondComponent.open &&
<form onSubmit={handleSubmit(onSubmit)}
<AutoCompleteSuggestion
control={control}
name="guest"
selection="id"
label="name"
items={items}
openSecondComponent={openSecondComponent}
/>
<AutoCompleteSuggestion
control={control}
name="contact"
selection="id"
label="name"
items={items2}
openSecondComponent={openSecondComponent}
/>
</form>
};
{secondComponent.open?
<SecondComponent/>: ''
};
</>
);
};
And this is the customised AutoComplete:
**AutoComplete**
import React, { useState } from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete, from "#material-ui/lab/Autocomplete";
import parse from "autosuggest-highlight/parse";
import match from "autosuggest-highlight/match";
import { Controller } from "react-hook-form";
import Button from "#material-ui/core/Button";
const AutoCompleteSuggestion = (props) => {
const [texts, setTexts] = useState('');
return (
<>
<Controller
name={props.name}
control={props.control}
render={({ onChange }) => (
<Autocomplete
options={props.items}
inputValue={texts} //NOT GETTING UPDATED BY STATE
debug={true}
getOptionLabel={(value) => value[props.label]}
noOptionsText = {
<Button onClick={()=> props.opensSecondComponent()}>
Add {texts} as a {props.implementation}
</Button>}
onChange={(e, data) => {
if (data==null){
onChange(null)
} else {
onChange(data[props.selection]); //THIS ONCHANGE WORKS
setTexts(data[props.label]) //THIS DOESN'T UPDATE STATE
}}
renderInput={(params) => (
<TextField
{...params}
onChange = { e=> setTexts(e.target.value)}
/>
)}
renderOption={(option, { inputValue }) => {
const matches = match(option[props.label1, inputValue);
const parts = parse(option[props.label], matches);
return (
<div>
{parts.map((part, index) => (
<span
key={index}
style={{ fontWeight: part.highlight ? 700 : 400 }}
>
{part.text}
</span>
))}
</div>
);
}}
/>
)}
/>
</>
);
};
export default AutoCompleteSuggestion;

TypeError: Cannot read property 'userName' of undefined in react

getting an error like "username undefined" don't know whats going on wrong coz i have declared username stiil getting error plz try to solve my error
https://ibb.co/7jGyCD1
This is my code where i got error
import React from 'react';
import './Message.css';
import {Card,Typography,CardContent} from '#material-ui/core';
const Message = ({message,userName}) => {
const isUser = userName === message.userName;
return (
<div className={`message ${isUser && 'message_user'}`}>
<Card className={isUser ? 'message_userCard' : 'message_guestCard'}>
{/* if the user is logged in then show the user card otherwise show the styiling */}
<CardContent>
<Typography
color='white'
varient='h5'
component='h2'>
<h2>{message.userName} : {message.message}</h2>
</Typography>
</CardContent>
</Card>
</div>
)
}
export default Message;
App.js
This is my app.js file where i wrote my all logic code and i used firebase for store our data in database backend
import React, { useEffect, useState } from 'react';
import './App.css';
import { Button, Input } from '#material-ui/core';
import FormControl from '#material-ui/core/FormControl';
import Message from './Message';
import db from './Firebase';
const App = () => {
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [userName,setuserName] = useState('');
// console.log(input);
// console.log(messages);
// console.log(userName);
//every single time when database change or add value in dayabase then onSnapshot run
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
doc.data()
}))
})
},[]);
useEffect(()=>{
setuserName(prompt('enter your name'))
},[])
//in above whenever my input value change inside useEffect code run
const sendMessage = (e) => {
e.preventDefault()
setMessages([...messages, {userName: userName, text:input}])
setInput('');
}
//usestate= variable in react its just a peace of code
//useffetc = it is block of code that can executed based on a condition
return (
<div className="App">
<form>
<h1>Welcome Ashish</h1>
<h2>Welcome {userName}</h2>
<FormControl>
<Input value={input} onChange={(e) => setInput(e.target.value)} />
<Button disabled={!input} variant='contained' color='primary' type='submit' onClick={sendMessage}>send message</Button>
</FormControl>
</form>
{
messages.map(message => (
<Message userName={userName} message={message}/>
))
}
</div>
);
}
export default App;
The first thing you need to ensure that is you need to map over the messages array only if it is present.Consider the code below. I have added a check to render the Message component only if messages are present.
import React, { useEffect, useState } from 'react';
import './App.css';
import { Button, Input } from '#material-ui/core';
import FormControl from '#material-ui/core/FormControl';
import Message from './Message';
import db from './Firebase';
const App = () => {
const [input, setInput] = useState('');
const [messages, setMessages] = useState([]);
const [userName,setuserName] = useState('');
// console.log(input);
// console.log(messages);
// console.log(userName);
//every single time when database change or add value in dayabase then onSnapshot run
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
doc.data()
}))
})
},[]);
useEffect(()=>{
setuserName(prompt('enter your name'))
},[])
//in above whenever my input value change inside useEffect code run
const sendMessage = (e) => {
e.preventDefault()
setMessages([...messages, {userName: userName, text:input}])
setInput('');
}
//usestate= variable in react its just a peace of code
//useffetc = it is block of code that can executed based on a condition
return (
<div className="App">
<form>
<h1>Welcome Ashish</h1>
<h2>Welcome {userName}</h2>
<FormControl>
<Input value={input} onChange={(e) => setInput(e.target.value)} />
<Button disabled={!input} variant='contained' color='primary' type='submit' onClick={sendMessage}>send message</Button>
</FormControl>
</form>
{
messages && messages.length && messages.map(message => (
<Message userName={userName} message={message}/>
))
}
</div>
);
}
export default App;
useEffect(()=>{
db.collection('messages').onSnapshot(snapshot=>{
setMessages(snapshot.docs.map(doc=>{
// 👇 you miss out the `return` here
return doc.data()
}))
})
},[]);
I think this could be the reason
Maybe when you are creating the use state for username.
Make sure its like this "setUserName". i dont know if its correct or wrong. I always write like this. Change Your Code According to it in your App.js.

How to dynamically show a list of data by React?

I am new to React and trying to display list of data with checkbox and inputbox. In details, I want to grab a series of data from database and put each record into a <div> like element with checkbox and inputbox. So I can check the record and change the data and then do the re-save action after clicking a button. Since the number of data will keep changing, how to make it dynamic? Also, how can I mark down which records are being checked and need to be saved? Thanks!
Code:
App.js:
import React from 'react';
import { useState, useEffect } from 'react';
import { Menu, Message, Button, Segment } from 'semantic-ui-react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
import Form from './Form';
export default function App(props){
const [currentDate, setNewDate] = useState(null);
const onChange = (event, data) => setNewDate(data.value);
const loadData= (event) => {
return (<Form date = {currentDate} />);
};
return (
<div className="App">
<div>
<Menu borderless>
<Menu.Item >
<div >
<img src={logo} alt="image" />
</div>
</Menu.Item>
</Menu>
<Segment>
<SemanticDatepicker onChange={onChange} />
<Button onClick={loadData}>Load Data</Button>
</Segment>
<Segment>>
</Segment>
//Here will diaplyed returned list of data after click button
</div>
</div>
)
};
Simple JSON response:
{
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}
You could use your data to build your form.
You need to build the state from your data.
Also, map your input fields with respect to your state.
If the state needs different input fields, you could define your input fields in deriveStateFromData.
You can check the example here
For Object.keys, you could check the docs here
import React from 'react';
const price = {
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}
function deriveStateFromData(data) {
let state = {}
Object.keys(data).forEach(key => {
state[key] = data[key]['PRICE']
})
return state;
}
function MyForm({ data }) {
const [form, setFormData] = React.useState(deriveStateFromData(data));
const handleChange = e => {
setFormData({ ...form, [e.target.name]: Number(e.target.value) });
}
console.log(form)
return (
<>
{Object.keys(form).map(key => {
return (
<div>
<label>{key}</label>
<input
name={key}
value={form[key]}
onChange={handleChange}
/>
</div>
)
})}
</>
)
}
const App = () => <MyForm data={price} />
export default App;

Resources