React: socket.io and useEffect not working properly - reactjs

So I'm learning socket.io with react, and I'm encountering an issue. What I want to happen is that when a user inputs something into the field and submits it, it makes the title of everyone's browser that input. However, if your text field is equal to the message spread with everyone (meaning it was probably you who clicked enter) it'll make your title 'You changed it'.
However, it seems that in io.on(message), when it checks if the input state is equal to the message, it always checks the initial value of the state against the message, which is ''.
I'm guessing this happens because useEffect is called at the beginning when the state is '', so it hard codes the io.on(message) to be what the state is at that time. I thought to fix this i'd just make input a part of useEffect's dependancy array, but that doesn't override the current io.on(message), it overloads it, so both are called. Don't know how to fix this!
import React from 'react'
import io from 'socket.io-client'
const SocketTest = (props: any) => {
const socket = io('http://localhost:4000')
const [input, setInput] = React.useState<string>('')
const handleNewMessage = (message: string) => {
console.log(input)
if (input !== message){
document.title = message
} else {
document.title = 'You changed it!'
}
}
React.useEffect(() => {
socket.on('message', (message: string) => {
handleNewMessage(message)
})
}, [])
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInput(e.target.value)
}
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
socket.emit('message', input)
}
return (
<div>
<h2 className="center">{ input }</h2>
<form onSubmit={ (e) => handleSubmit(e) }>
<input type="text" placeholder="Enter text" onChange={ (e) => handleChange(e) }/>
</form>
</div>
)
}
export default SocketTest
I'm also sure I could fix this by broadcasting the message instead of emitting it, but I'm moreso trying to learn how to fix that issue.

Related

Unexpected behavior with my native react input validation

I created an input field which am trying to validate
const [name, setName] = useState('');
const [formErrors, setFormErrors] = useState({});
<p>Name</p>
<input
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Error> {formErrors.name}</Error>
<Button
onClick={handleSubmit}
>
Submit
</Button>
OnClick of the submit button it checks if the name field is empty in the handleSubmit function
const validate = (values) => {
const errors = {};
if (!values.name) {
errors.name = 'Name is required!';
}
return errors;
};
const handleSubmit = async (e) => {
const val = {name};
setFormErrors(validate(val));
if (Object.keys(formErrors).length === 0) {
console.log('No empty');
}else{
console.log('Empty');
}
};
The issue am having is that it lags behind in response. For example if the name field is empty it console's Not empty, on first click of the buttton, if I then click the button again it then console's the correct data which is 'Empty'.
This is becasue the state is not set until the component is being re-render therefore the formErrors state is {} until the handle function ends. Create a new constant to hold the errors and use these to console the outcome instead of the state itself if you still need to do something during the event, however use the state inside the JSX to render correctly since state will have been changed by then.
const handleSubmit = async (e)=>{
const val = {name};
const errors = validate(val)
setFormErrors(errors);
if (Object.keys(errors).length === 0) {
console.log('No empty');
}else{
console.log('Empty');
}
}

How to read the value of updated array while using useState hook in react?

I am trying to build my 1st ToDo-list-app in React & I can't seem to successfully read the data from my "list" array which I've initiated using useState([]).
The issue I'm facing is - if my 1st entered task is "1-Eat Breakfast" & I click on the add button, I'm getting an empty array in console.log,
When I enter my 2nd task lets say - "2-Hit the gym"; that's when my previous task is getting console logged. So, apparently I am unable to read the latest state of my array - "list".
Can you please point out what I am doing wrong in the code given below?
Thanks a lot.
import { useState } from "react";
const ToDo = () => {
const [task, setTask] = useState("");
const [list, setList] = useState([]);
const readData = (event) => {
event.preventDefault();
setTask(event.target.value);
};
const showList = (event) => {
event.preventDefault();
setList([...list, task]);
console.log(list);
};
return (
<div>
<div>
<form onSubmit={showList}>
<input type="text" value={task} onChange={readData} />
<button>Add to List</button>
</form>
</div>
</div>
);
};
export default ToDo;
can you change
const showList = (event) => {
event.preventDefault();
setList([...list, task]);
console.log(list);
};
to
const showList = (event) => {
event.preventDefault();
list.push(task);
setList(list);
console.log(list);
};

Irritating API bug in react, cannot figure it out

I have an API call set up with two search buttons from one input box. Each button adds something using state to the api call which the code below should demonstrate. The calls both work fine independently and display the correct information.
If a user has clicked the 'wild cards' button the results show but then on clicking the 'standard set' button the results don't re-render the correct results until the button is pressed a second time (vice versa for both buttons).
I have removed the un-related code as to condense the question
Any help appreciated
Home.js - with api call, state and functions passed down as props to the searchBar
export const Home = () => {
const [searchTerm, setSearchTerm] = useState('')
const [wild, setWild] = useState('')
let accessToken;
const getCards = async() => {
try {
await getToken()
const response = await fetch(`https://eu.api.blizzard.com/hearthstone/cards/?collectible=1${wild}&textFilter=${searchTerm}&locale=en-US$access_token=${accessToken}`, {
headers: {
Authorization: `Bearer ${accessToken}`
}})
const data = await response.json()
const {cards} = data
if (cards){
const newCards = cards.map((card) => { ** some data i want ** }
setCards(newCards)
}
} catch (error) {
console.log(error)
}
}
return (
<SearchBar getCards = {getCards}
setWild = {setWild}
setSearchTerm = {setSearchTerm} />
</div>
</div>
)
}
SearchBar Component - again condensed for the sake of this question
export const SearchBar = (props) => {
const searchBox = useRef('')
const handleClickWild = (e) => {
e.preventDefault()
props.setWild('')
props.getCards()
}
const handleClickStandard = (e) => {
e.preventDefault()
props.setWild('&set=standard')
props.getCards()
}
const handleChange = (e) => {
props.setSearchTerm(e.target.value)
}
return (
<form>
<input className = 'input-search'
type = 'text'
placeholder = 'Search for Cards by Name...'
ref = {searchBox}
onChange = {handleChange} />
<div className = 'search-buttons'>
<input type = 'submit' className = 'button-search' onClick = {handleClickWild} value = 'Wild Cards' />
<input type = 'submit' className = 'button-search' onClick = {handleClickStandard} value = 'Standard Set' />
</div>
</form>
)
}
You have to use useEffect hook here.
You can use wild in dependency array and whenever you change the value of searchTerm use effect will automatically call your getCards function.
As you mentioned in the comment you want to show changes when user search anything then keep the wild in simple variable and add search term in useEffect and if you want you can add both in the useEffect dependency array
useEffect(()=> {
getCards()
}, [searchTerm])
Just remove explicite calls of props.getCards after setting wild from SearchBar component.
I solved this with useEffect as suggested but I added an 'if (hasSearched)' as state value of is the user had searched previously to prevent the API auto calling on page load

React hook not setting the state

Hey all trying to use a useState react hook to set a state but it does not work, I gone through the official documentation
Seems like i have followed it correctly but still cannot get the hook to set the state:
const [search, setSearch] = useState('');
const { films } = props;
const matchMovieSearch = (films) => {
return films.forEach(item => {
return item.find(({ title }) => title === search);
});
}
const handleSearch = (e) => {
setSearch(e.target.value);
matchMovieSearch(films);
}
<Form.Control
type="text"
placeholder="Search Film"
onChange={(e) => {handleSearch(e)}}
/>
Search var in useState is allways empty even when i debug and can see that e.target.value has to correct data inputed from the html field
setSearch is an async call, you won't be able to get the search immediately after setting the state.
useEffect is here for rescue.
useEffect(() => {
// your action
}, [search]);
Are you sure you are using the hooks inside a component, hooks can only be used in a Functional React Component.
If that is not the case, there must be something wrong with the Form.Control component, possibly like that component did not implement the onChanged parameter properly.
This is the one I tested with the html input element, and it is working fine. I used the useEffect hook to track the changes on the search variable, and the you can see that the variable is being properly updated.
https://codesandbox.io/s/bitter-browser-c4nrg
export default function App() {
const [search, setSearch] = useState("");
useEffect(() => {
console.log(`search was changed to ${search}`);
}, [search]);
const handleSearch = e => {
setSearch(e.target.value);
};
return (
<input
type="text"
onChange={e => {
handleSearch(e);
}}
/>
);
}

React hook api state is sometimes empty

I am using hook api for managing state, the problem is that state is sometimes empty in handler fucntion.
I am using component for manage contenteditable (using from npm) lib. You can write to component and on enter you can send event to parent.
See my example:
import React, { useState } from "react"
import css from './text-area.scss'
import ContentEditable from 'react-contenteditable'
import { safeSanitaze } from './text-area-utils'
type Props = {
onSubmit: (value: string) => void,
}
const TextArea = ({ onSubmit }: Props) => {
const [isFocused, setFocused] = useState(false);
const [value, setValue] = useState('')
const handleChange = (event: React.FormEvent<HTMLDivElement>) => {
const newValue = event?.currentTarget?.textContent || '';
setValue(safeSanitaze(newValue))
}
const handleKeyPress = (event: React.KeyboardEvent<HTMLDivElement>) => {
// enter
const code = event.which || event.keyCode
if (code === 13) {
console.log(value) // THERE IS A PROBLEM, VALUE IS SOMETIMES EMPTY, BUT I AM SURE THAT TEXT IS THERE!!!
onSubmit(safeSanitaze(event.currentTarget.innerHTML))
setValue('')
}
}
const showPlaceHolder = !isFocused && value.length === 0
const cls = [css.textArea]
if (!isFocused) cls.push(css.notFocused)
console.log(value) // value is not empty
return (
<ContentEditable
html={showPlaceHolder ? 'Join the discussion…' : value}
onChange={handleChange}
className={cls.join(' ')}
onClick={() => setFocused(true)}
onBlur={() => setFocused(false)}
onKeyPress={handleKeyPress}
/>
)
}
export default React.memo(TextArea)
Main problem is that inside handleKeyPress (after enter keypress) is value (from state) empty string, why? - in block console.log(value) // THERE IS A PROBLEM, VALUE IS SOMETIMES EMPTY, BUT I AM SURE THAT TEXT IS THERE!!! I don't understand what is wrong??
The value is empty, because onChange doesn't actually change it, which means
const newValue = event?.currentTarget?.textContent || '';
this line doesn't do what it's supposed to. I think you should read the target prop in react's synthetic events instead of currentTarget. So, try this instead
const newValue = event.target?.value || '';
Hope this helps.

Resources