Cannot read property 'publish' of undefined in react - reactjs

Within my handleSubmit method if I hardcode the message and publish the method works as intended. However if I replace "hello stomp" with the input state or submit with any input at all I get Uncaught TypeError: Cannot read property 'publish' of undefined" any insight here will be greatly appreciated
export const Comms = () => {
const [messages, setMessages] = useState();
const [input, setInput] = useState("");
const client = new Client();
client.configure({
brokerURL: "ws://localhost:2019/socket",
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
onConnect: function () {
client.subscribe("/topic/messages", function (msg) {
console.log("WS-MESSAGE: ", msg.body);
});
},
});
const handleSubmit = (event) => {
client.publish({ destination: "/topic/messages", body: "Hello stomp" });
event.preventDefault();
};
useEffect(() => {
client.activate();
}, []);
return (
<div className="comms-cont">
<h1 className="comms-header">Messaging</h1>
<form onSubmit={handleSubmit} className="form-1">
<input
className="forminput"
type="text"
name="message"
onChange={(e) => {
setInput(e.target.value);
}}
/>
</form>
</div>
);
};

You want to use a controlled input component. Add the prop value to the input like this:
<input
className="forminput"
type="text"
name="message"
value={input}
onChange={(e) => {
setInput(e.target.value);
}}
/>

Related

React UI/dom is not updating after data insertion in json-server

I am learning React from few days and I am trying to learn Axios, Everything worked fine until I tried to insert data, which I successfully inserted but My React Page did not updated contact list immediately.
HERE's MY CODE:
App.js
import Axios from "axios";
import React, { useEffect, useState } from "react";
import Add__Contact from "./api/Add__Contact";
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
//get all availbale contacts
useEffect(() => {
// get all contacts async
async function getUsers() {
Axios.get(url).then((response) => {
setContacts(response.data);
});
}
getUsers();
console.log(contacts);
// get all contacts non-async
// Axios.get(url).then((response) => {
// setContacts(response.data);
// });
}, []);
//add new contact to server
const addContact = () => {
const saveRes = Add__Contact({ name, phone });
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
export default App;
Add__Contact.js
import Axios from "axios";
const Add__Contact = async ({ name, phone }) => {
Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
}).then(function (res) {
// console.log(res);
});
};
export default Add__Contact;
db.json
{
"contacts": [
{
"name": "Max",
"phone": "123456",
"id": 1
},
{
"name": "John",
"phone": "13454",
"id": 2
},
{
"name": "Candy",
"phone": "1245781245",
"id": 3
}
]
}
I am not sure why it's not updating list automatically, I thought useEffect will run everytime I click and call Add__Contact(). Can you please tell me what did i missed or doing wrong?
I am not sure if useEffect hook is good for what I want to achieve or not, so please guide me. Thank you in advance.
data insertion is working fine, but after I insert it, it's not updating ui, even if I am fetching data inside useEffect
Your useEffect hook is only ran once - when the component mounts. This is because you have given it an empty dependency array (the 2nd argument).
The dependency array determines when the effect function will run. If its empty, it will only run when the component is mounted (displayed for the very first time). If you add something in the array, the effect will run on mount, and whenever the provided value changes.
In your case, you have an event (the click event from the Add to Contacts button) after which you want your data to be fetched again. But you also want to fetch data when the page loads.
One way to do it is something like this:
const Add__Contact = async ({ name, phone }) => {
// Return the Promise returned from the Axios call
return Axios({
method: "post",
url: "http://localhost:3006/contacts",
headers: {
"Content-Type": "application/json",
},
data: {
name,
phone,
},
});
};
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const url = "http://localhost:3006/contacts";
// Add a function to fetch contacts
const fetchContacts = async () => {
const res = await Axios.get(url);
setContacts(res.data);
};
// Effect that fetches contacts when the component loads
useEffect(() => {
fetchContacts();
}, []);
//add new contact to server
const addContact = async () => {
// await the Promise returned
const saveRes = await Add__Contact({ name, phone });
// Fetch the contacts list again
await fetchContacts();
};
// view
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};
So you'r Contacts Array is not updated .Even you got a data from axios call .
Like if axios is returning data then i think you'r state in not updating then you have to use
setContacts((prv)=>[...prv,...res.data])
If you'r facing problem on Add time . Then make a separate function then use that in useEffect() && your ADD_Contact() .
const App = () => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [contacts, setContacts] = useState([]);
const getContacts = async () => {
const res = await Axios.get('http://localhost:3006/contacts');
setContacts(res.data);
};
useEffect(() => {
getContacts();
}, []);
const addContact = async () => {
const saveRes = await Add__Contact({ name, phone });
await getContacts();
};
return (
<div>
<h4>Add contact</h4>
<div>
<input type="text" name="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="name here" />
<br />
<br />
<input
type="text"
name="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="Phone here"
/>
<br />
<br />
<button onClick={addContact}>Add to Contact</button>
</div>
<hr />
<h4>List of Contacts</h4>
<div>
{contacts.map((contact) => {
return (
<div key={contact.id}>
<span>{contact.name} : </span>
<span> {contact.phone}</span>
</div>
);
})}
</div>
</div>
);
};

Problem updating data in an api with mui TextField inputs

I'm making a post admin tool with jsonplaceholder api and I'm having a problem with the put method. How can I update the text input modifications? It's not working even with the fixed data.
const EditPost = ({ match }) => {
const [state, setState] = useState({
title: "",
body: "",
});
const [error] = useState("");
const { id } = match.params;
const [post, setPost] = useState(null);
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: "Hello World!",
body: "Post updated."
})
.then((response) => {
setPost(response.data);
});
}
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
defaultValue={post.title || ""}
name="title"
type="text"
onChange="" />
<TextField
id="outlined-multiline-flexible"
label="Body"
defaultValue={post.body || ""}
name="body"
type="text"
onChange="" />
<Button
type="submit">
Update Post
</Button>
I would suggest you restructure your component as such:
const EditPost = ({ match }) => {
const [error] = useState("");
const { id } = match.params;
const [postTitle, setPostTitle] = useState("");
const [postBody, setPostBody] = useState("");
useEffect(() => {
axios
.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
setPost(response.data);
});
}, []);
const updatePost = () => {
axios
.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
title: postTitle,
body: postBody,
})
.then(({ data }) => {
setPostTitle(data.title);
setPostBody(data.body);
});
};
return (
<form noValidate autoComplete="off" onSubmit={updatePost}>
<TextField
id="outlined-basic"
label="Title"
value={postTitle}
name="title"
type="text"
onChange={(e) => setPostTitle(e.target.value)}
/>
<TextField
id="outlined-multiline-flexible"
label="Body"
value={postBody}
name="body"
type="text"
onChange={(e) => setPostBody(e.target.value)}
/>
<Button type="submit">Update Post</Button>
</form>
);
};
To be able to update the state as the user types, you need to control your form fields. The way to do this is to supply each field with a value prop, and use the onChange prop to update the state of the fields. This is the recommended by the mui team.
Furthermore, we have moved our state from a single object (which is reminiscent of the days of class components), to separate state objects (postBody and postTitle) which should reduce the number of times parts of the component have to re-render.

react-phone-number-input code sample using http API

Just asking how to get this and put it to my API. Here's a link: https://web.5writer.com/user/signup
{
"countryCallingCode": "374",
"nationalNumber": "23131223",
"number": "+37423131223",
"country": "AM"
}
This is the body of my API
{
dial_code,
mobile,
iso_code
}
This is my code
export default function Home() {
const toast = useToast()
const router = useRouter();
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const [dial_code, setDial] = useState('');
const [mobile, setMobile] = useState('');
const [iso_code, setIso] = useState('');
async function handleSubmit (e) {
e.preventDefault();
setLoading(true);
fetch(`https://web.5writer.com/user/signup`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dial_code,
mobile,
iso_code,
}),
})
.then((res) =>
res.json().then((body) => ({
status: res.status,
body,
}))
)
.then((resp) => {
console.log(resp);
setLoading(false);
if (resp.body.status === true) {
setDial('');
setMobile('');
setIso('');
toast({
title: 'Success!',
description: resp.body.message,
status: 'success',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
router.push('../AllOrders/dashboard');
}
else {
toast({
title: 'ERROR!',
description: resp.body.message,
status: 'error',
duration: 6000,
isClosable: true,
position: 'top',
variant: 'left-accent',
});
}
})
}
return (
<div>
{success && <Notification />}
<main>
<Container >
<Box
w='17.8em'
p={0}
borderRadius='5px'
mt={3}
mb={-4}
mx='auto'
pos='relative'
marginLeft='-1em'
>
{loading && (
<Progress
pos='absolute'
top='0'
left='0'
width='100%'
isIndeterminate
borderTopLeftRadius='6px'
borderTopRighRtadius='6px'
size='sm'
colorScheme='blue'
/>
)}
<form onSubmit={handleSubmit}>
<FormControl className="">
<PhoneNumber
placeholder="enter phone number"
value={dial_code}
onChange={(e) => setDial(e.target.value)}
/>
</FormControl>
<div className="form-group2 d-md-flex">
<div className="w-50 text-left">
<input type="checkbox" className="checkL"/>
<div className="remember">
I have read the <a className="terms">Terms and Condition</a>
</div>
</div>
</div>
<Button
type='submit'
mt='0'
size='sm'
colorScheme='#2CBEFF'
disabled={loading}
pos='relative'
className="lbutton"
>
Register
{/* {loading && <Spinner pos='absolute' color='red.500' />} */}
</Button>
<div className="form-group3">
<p className="text-center">Already have an account?
<Link href="/Login"><a data-toggle="tab" className="Log">Log In</a></Link></p>
</div>
</form>
</Box>
</Container>
</main>
</div>
);
}
This code is working but the problem is I only got one data using onChange. Is it possible to use 3 onChange? or is there any method to get 3 data in just one input.
Give me a piece of advice thank you.
It's still not entirely clear what your issue is, but based on the comments it seems you want a single state variable and change handler to manage 3 inputs. You generally accomplish this by associating a name attribute with each input. The name attribute is accessed via the onChange event and can update the specific nested state.
Example:
const initialState = {
country: "",
countryCallingCode: "",
number: ""
};
function App() {
const [{ country, countryCallingCode, number }, setState] = React.useState(
initialState
);
const changeHandler = (e) => {
const { name, value } = e.target; // <-- destructure from event
setState((state) => ({
...state,
[name]: value // <-- use name as dynamic key
}));
};
const submitHandler = (e) => {
e.preventDefault();
const data = {
dial_code: countryCallingCode,
number,
country
};
setState(initialState);
console.log(data);
};
return (
<div className="App">
<form onSubmit={submitHandler}>
<div>
<label>
Country Code
<input
type="text"
value={countryCallingCode}
name="countryCallingCode"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Number
<input
type="text"
value={number}
name="number"
onChange={changeHandler}
/>
</label>
</div>
<div>
<label>
Country
<input
type="text"
value={country}
name="country"
onChange={changeHandler}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<App />,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
Update
If you want to use the PhoneInput component you need to map its onChange handler to your own since it passes directly the input value to the handler.
const changeHandler = (e) => {
const { name, value } = e.target;
setState((state) => ({
...state,
[name]: value
}));
};
Here the changeHandler is expecting an onChange event object. You can pass any object you like, and so long as it has the correct shape and properties the handler can handle it.
<PhoneInput
value={number}
name="number"
onChange={(value) =>
changeHandler({
target: {
name: "number",
value
}
})
}
/>
Update 2
Ok, I think I understand what you're after now. You want just a single phone number input and then to parse the country code, country, and phone number from the single state.
Check parsePhoneNumber
There's no need for any custom onChange handlers, just update state with the PhoneInput value and when you are ready, parse the state.
const [state, setState] = React.useState(null);
const submitHandler = (e) => {
e.preventDefault();
const {
countryCallingCode: dial_code,
country: iso_code,
number: mobile
} = parsePhoneNumber(state);
const data = {
dial_code,
mobile,
iso_code
};
setState(null);
// do with data now what you need
};
...
<PhoneInput value={state} onChange={setState} />
hi that's a little dirty but you can create your state somethings like this
let [values,setValues] = useState({phoneNumber : '', dial:'', code:''});
let [inputState, setInputState] = useState('phoneNumber');
const onInputChange = (e) => {
const { target : { value } } = e;
setValues(preventValues => ({...preventValues, inputState : value}))
}
const handleSubmit = (inputStateName) => {
// do your functionality then
setInputState(inputStateName);
}

how to check if input is clear (react redux)

im trying to create a discord clone, and when i hit enter when the input is empty it shows an empty message, any idea how to prevent that from happening? im new to react, firebase and redux.
const user = useSelector(selectUser);
const channelId = useSelector(selectChannelId);
const channelName = useSelector(selectChannelName);
const [input, setInput] = useState("");
const [messages, setMessages] = useState([]);
useEffect(() => {
if (channelId) {
db.collection('channels')
.doc(channelId)
.collection('messages')
.orderBy('timestamp', 'desc')
.onSnapshot((snapshot) =>
setMessages(snapshot.docs.map((doc) => doc.data()))
);
}
}, [channelId])
const sendMessage = (e) => {
e.preventDefault();
db.collection('channels').doc(channelId).collection('messages').add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
message: input,
user: user,
});
setInput('')
}
return (
<div className="chat">
<ChatHeader channelName={channelName}/>
<div className="chat__messages">
{messages.map((message) => (
<Message
timestamp={message.timestamp}
message={message.message}
user={message.user}
/>
))}
</div>
<div className="chat__input">
<AddCircleIcon fontSize="large"/>
<form>
<input
id="input"
value={input}
disabled={!channelId}
onChange={e => setInput(e.target.value)}
placeholder={`Message #${channelName}`}
/>
<button onClick={sendMessage} className="chat__inputButton" type="submit">
Send Message
</button>
</form>
<div className="chat__inputIcons">
<CardGiftcardIcon fontSize="large"/>
<GifIcon fontSize="large"/>
<EmojiEmotionsIcon fontSize="large"/>
</div>
</div>
</div>
)
}
export default Chat
All you need to do is prevent your submit function from calling firebase if your input is empty.
const sendMessage = (e) => {
e.preventDefault();
if (input.length <= 0) return; // This will end the function here if your input is empty
db.collection('channels').doc(channelId).collection('messages').add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
message: input,
user: user,
});
setInput('')
}
Just check if is some value in it. Empty string is falsable so you can check it in one line.
<input onChange={e => e.target.value && setInput(e.target.value)}/
But in your case probably you send your form on enter. So you can block sending form with empty input by setting validation pattern
<input pattenr=".+" />
or make validation in sumbmit function.

Handle an input with React hooks

I found that there are several ways to handle user's text input with hooks. What is more preferable or proper way to handle an input with hooks? Which would you use?
1) The simplest hook to handle input, but more fields you have, more repetitive code you have to write.
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
events:
onChange={event => setPassword(event.target.value)}
onChange={event => setUsername(event.target.value)}
2) Similar to above example, but with dynamic key name
const [inputValues, setInputValues] = useState({
username: '', password: ''
});
const handleOnChange = event => {
const { name, value } = event.target;
setInputValues({ ...inputValues, [name]: value });
};
event:
onChange={handleOnChange}
3) An alternative to useState, and as said on ReactJS docs, useReducer is usually preferable to useState.
const [inputValues, setInputValues] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{username: '', password: ''}
);
const handleOnChange = event => {
const { name, value } = event.target;
setInputValues({ [name]: value });
};
event:
onChange={handleOnChange}
4) useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
const [inputValues, setInputValues] = useState({
username: '', password: ''
});
const handleOnChange = useCallback(event => {
const { name, value } = event.target;
setInputValues({ ...inputValues, [name]: value });
});
event:
onChange={handleOnChange}
How about writing a reusable function that returns the input value ... and the <input> itself:
function useInput({ type /*...*/ }) {
const [value, setValue] = useState("");
const input = <input value={value} onChange={e => setValue(e.target.value)} type={type} />;
return [value, input];
}
That can then be used as:
const [username, userInput] = useInput({ type: "text" });
const [password, passwordInput] = useInput({ type: "text" });
return <>
{userInput} -> {username} <br />
{passwordInput} -> {password}
</>;
This is how i'm using right now:
const [inputValue, setInputValue] = React.useState("");
const onChangeHandler = event => {
setInputValue(event.target.value);
};
<input
type="text"
name="name"
onChange={onChangeHandler}
value={inputValue}
/>
Yes you can handle react hooks with useState()
import React, {useState} from 'react'
export default () => {
const [fName, setfName] = useState('');
const [lName, setlName] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const submitValue = () => {
const frmdetails = {
'First Name' : fName,
'Last Name' : lName,
'Phone' : phone,
'Email' : email
}
console.log(frmdetails);
}
return(
<>
<hr/>
<input type="text" placeholder="First Name" onChange={e => setfName(e.target.value)} />
<input type="text" placeholder="Last Name" onChange={e => setlName(e.target.value)} />
<input type="text" placeholder="Phone" onChange={e => setPhone(e.target.value)} />
<input type="text" placeholder="Email" onChange={e => setEmail(e.target.value)} />
<button onClick={submitValue}>Submit</button>
</>
)
}
Here's how I do it (assuming your inputs must be inside a form):
I have a BasicForm component that I use.
It stores all the inputs state into an object into a single useState() call.
It passes via useContext() the inputs state along with an onChange() function and a function setInputInitialState() for the inputs to set their initial state when they are first mounted. It also passes onFocus, onBlur, and it has functions to validate fields which I'm not showing here to simplify the code.
This way I can easily create a form with as many inputs as I want, like:
<BasicForm
isSubmitting={props.isSubmitting}
submitAction={ (formState) =>
props.doSignIn(formState) }
>
<TextInput
type='email'
label='Email'
name='email'
placeholder='Enter email...'
required
/>
<TextInput
type='password'
label='Password'
name='password'
placeholder='Enter password...'
min={6}
max={12}
required
/>
<SubmitButton
label='Login'
/>
</BasicForm>
BasicForm.js
import FormContext from './Parts/FormContext';
function BasicForm(props) {
const [inputs, setInputs] = useState({});
function onChange(event) {
const newValue = event.target.value;
const inputName = event.target.name;
setInputs((prevState)=> {
return({
...prevState,
[inputName]: {
...prevState[inputName],
value: newValue,
dirty: true
}
});
});
}
function setInputInitialState(
inputName,
label='This field ',
type,
initialValue = '',
min = false,
max = false,
required = false) {
const INITIAL_INPUT_STATE = {
label: label,
type: type,
onFocus: false,
touched: false,
dirty: false,
valid: false,
invalid: false,
invalidMsg: null,
value: initialValue,
min: min,
max: max,
required: required
};
setInputs((prevState) => {
if (inputName in prevState) {
return prevState;
}
return({
...prevState,
[inputName]: INITIAL_INPUT_STATE
});
});
}
return(
<FormContext.Provider value={{
onChange: onChange,
inputs: inputs,
setInputInitialState: setInputInitialState,
}}>
<form onSubmit={onSubmit} method='POST' noValidate>
{props.children}
</form>
</FormContext.Provider>
);
}
TextInput.js
The inputse use the useEffect() hook to set their initial state when they're mounted.
function TextInput(props) {
const formContext = useContext(FormContext);
useEffect(() => {
console.log('TextInput useEffect...');
formContext.setInputInitialState(
props.name,
props.label,
props.type,
props.initialValue,
props.min,
props.max,
props.required
);
},[]);
return(
<input
type={props.type}
id={props.name}
name={props.name}
placeholder={props.placeholder}
value={([props.name] in formContext.inputs) ?
formContext.inputs[props.name].value
: props.initialValue || ''}
onChange={formContext.onChange}
onFocus={formContext.onFocus}
onBlur={formContext.onBlur}
>
</input>
</div>
{([props.name] in formContext.inputs) ?
formContext.inputs[props.name].invalidMsg && <div><span> {formContext.inputs[props.name].invalidMsg}</span></div>
: null}
</div>
);
...
}
function App(){
const [name, setName] = useState("");
const [istrue, Setistrue] = useState(false);
const [lastname,setLastname]=useState("");
function handleclick(){
Setistrue(true);
}
return(
<div>
{istrue ? <div> <h1>{name} {lastname}</h1> </div> :
<div>
<input type="text" placeholder="firstname" name="name" onChange={e =>setName(e.target.value)}/>
<input type="text" placeholder="lastname" name="lastname" onChange={e =>setLastname(e.target.value)}/>
<button type="submit" onClick={handleclick}>submit</button>
</div>}
</div>
)
}
}
You may want to consider a form library like Formik

Resources