type hereimport './App.css';
import { useState } from 'react';
function App() {
const [text, setText] = useState()
const People = {
name: 'jack'
}
const peoples = People.map(() => ({
nick: People.name
})
)
const funct = () =>{
if (text === peoples.nickname) {
console.log('worked')
} else {
console.log('not worked')
}
}
return (
<div>
<input onChange={(event) => {setText(event.target.value)}}/>
<h1>{text}</h1>
<br />
<button onClick={funct}>Click</button>
</div>
);
}
export default App;
I'm hoping that you can solve my question,and show my at what point am i wrong. Thank you very much. I've just tried to map the text and it still doesn't work and also i've tried to make the text as an object but it still doesn't work.
You are comparing an array with a string and because of this, it's not working.
you need to update your funct to something like this:
const funct = () =>{
peoples.forEach((p) => {
if (p.nickname === text) {
console.log('worked')
} else {
console.log('not worked')
}
})
}
When trying to create a array of people you try to use map on a object. This is not possible since you can only use map on a array.
To fix this you could do something like this to create a array with a new object in it which contains a property nick with the value of People.name
const peoples = [{ nick: People.name }];
Then in the if statement you're checking for peoples.nickname while we just created a object with a property called nick.
if (text === peoples.nick) {
...
}
Now this in not yet going to work. Like mentioned in the comments you're comparing a string with a array. To solve this you can use some kind of loop
const funct = () => {
for (const people of peoples) {
if (text === people.nick) {
console.log("worked");
} else {
console.log("did not work");
}
}
};
Or we can use the every function to check if all the people in the array have match the text value
const funct = () => {
const allPeoplesValidNick = peoples.every((people) => people.nick === text);
if (allPeoplesValidNick) {
console.log("worked");
} else {
console.log("not worked");
}
};
Related
I am trying to make a socket io simple chat app and the connection works fine. The problems comes with the const renderChat. The console.log is read and correctly printed (with the value in the textbox) also if I put a static text it is also displayed on the frontend, however, for some reason it doesn't print
{msg.data["message"]}
which has to print each value in an array. Also I am almost certain that the array is emptied every time and the useEffect doesn't work properly but I can't really test that yet. Would be glad if I get solution to both problems!
import './App.css';
import io from 'socket.io-client'
import { useEffect, useState } from 'react'
import React from 'react';
import ReactDOM from "react-dom/client";
const socket = io.connect("http://localhost:3001");
function App() {
const [message, setMessage] = useState("");
const [chat, setChat] = useState([]);
const sendMessage = () => {
socket.emit("send_message", { message });
};
const renderChat = () => {
return (
chat.forEach(msg => {
<h3>{msg.data["message"]}</h3>
console.log(msg.data)
})
)
}
useEffect(() => {
socket.on("receive_message", message => {
setChat([...chat, message]);
});
}, [socket])
return (
<div className="App">
<input placeholder="Message..." onChange={(event) => {
setMessage(event.target.value);}}
/>
<button onClick={sendMessage}>Send Message</button>
<h1>Message:</h1>
{renderChat()}
</div>
);
}
export default App;
EDIT:
Thanks to Ibrahim Abdallah, now the printing works. The only thing that doesn't work is as I feared, storing the information. Here is the piece of code
useEffect(() => {
socket.on("receive_message", message => {
setChat([...chat, message]);
});
}, [socket])
For some reason when I enter for example "text1" it saves it but then if I enter "text2" it overwrites the first value "text1".
modify your renderChat function to use map instead and the console log message should be before the return statement of the map function
const renderChat = () => {
return (
chat.map(msg => {
console.log(msg.data)
return (
<h3>{msg.data["message"]}</h3>
)
})
)
}
Please use map instead of forEach
const renderChat = () => {
return (
chat.map(msg => {
<h3>{msg.data["message"]}</h3>
console.log(msg.data)
})
)
}
Use map instead of forEach
map actually returns an array of the returned results.
forEach on the other hand does not return anything.
const renderChat = () => {
return (
chat.map(msg => {
console.log(msg.data);
return <h3>{msg.data["message"]}</h3>;
})
)
}
What I am trying to achieve is after fitlerRequest function called filterListValue should manipulate.
But what happening is on radioHandleChange it's start manipulating the filterListValue.
I think they have the same memory reference but how to make a copy of it?
export default function CustomFilter(props) {
const { filterListValue, setFilterListValue } = props;
const [radioValue, setRadioValue] = useState(filterListValue)
const radioHandleChange = (e, list) => {
setRadioValue(radioValue => {
let copy = [...radioValue]
copy[indexChange].value = e.target.value
copy[indexChange].id = list.id
return copy
});
}
const filterRequest = () => {
setFilterListValue(radioValue)
handleClose()
};
}
Just make copies using .... Although not mentioned, looks like props.filterListValue is an array. So create a copy when starting with useState hook.
Also, when setting radio value, make a copy of the item (since it is an object) so you are not mutating the state by mistake.
const { filterListValue, setFilterListValue } = props;
const [radioValue, setRadioValue] = useState([...props.filterListValue])
const radioHandleChange = (e, list) => {
setRadioValue(radioValue => {
let copy = [...radioValue]
let copyItem = {...copy[indexChange]};
copyItem[indexChange].value = e.target.value
copyItem[indexChange].id = list.id
return copyItem
});
}
PS:
You can destructure inside the function argument parenthesis. It is common practice:
export default function CustomFilter({filterListValue, setFilterListValue}) {
On my current application, if a user tries to enter an existing name that has a different number, it will prompt the user if they want to update that entry with the new number. If yes, the entry is updated using an axios PUT request. My issue is that I can only get it to change on the front end by reloading the page (it updates successfully on db.json) instead of it updating immediately after the user confirms. On my useEffect method I tried adding [persons] as the second argument and it seemed to work, but found out that it loops the GET requests infinitely. I have a similar function for when deleting an entry so I'm sure it must be something that has to be added to setPersons
Update methods
const addEntry = (event) => {
event.preventDefault();
const newPersonEntry = {
name: newName,
number: newNumber,
}
const all_names = persons.map(person => person.name.toUpperCase())
const all_numbers = persons.map(person => person.number)
const updatedPerson = persons.find(p => p.name.toUpperCase() === newName.toUpperCase())
const newPerson = { ...updatedPerson, number: newNumber };
if (newName === '') {
alert('Name entry cannot be blank')
return
}
if (newNumber === '') {
alert('Number entry cannot be blank')
return
}
if (all_numbers.includes(newNumber)) {
alert('That number already exists')
return
}
if (newNumber.length < 14) {
alert('Enter a valid number')
return
}
if (all_names.includes(newName.toUpperCase())) {
if (window.confirm(`${newName} already exists, replace number with the new one?`)) {
console.log(`${newName}'s number updated`)
personService
.update(updatedPerson.id, newPerson)
.then(res => {
setPersons() //something here
})
return
}
return
}
personService
.create(newPersonEntry)
.then(person => {
setPersons(persons.concat(person))
setNewName('')
setNewNumber('')
})
}
//PUT exported as personService
const update = (id, newObject) => {
const request = axios.put(`${baseURL}/${id}`,newObject)
return request.then(response => response.data)
}
Other code
const App = () => {
const [persons, setPersons] = useState([])
useEffect(() => {
personService
.getAll()
.then(initialPersons => {
setPersons(initialPersons)
})
}, [])
...
//Display method
const filteredNames = persons.filter(person => person.name.toLowerCase().includes(filter.toLowerCase()))
const row_names = () => {
return (
filteredNames.map(person =>
<p key={person.id}>{person.name} {person.number} <button onClick={() => handleDelete(person)}>delete</button></p>));
}
...
//Render
return (
<div>
<h2>Phonebook</h2>
<h2>Search</h2>
<SearchFilter value={filter} onChange={handleFilterChange} />
<h2>Add Entry</h2>
<Form onSubmit={addEntry}
name={{ value: newName, onChange: handleNameChange }}
number={{ value: newNumber, onChange: handleNumberChange }}
/>
<h2>Numbers</h2>
<DisplayPersons persons={row_names()} />
</div>
)
}
The solution here is a little bit tricky but doable . You need to split your logic into two parts like this :
const [dataChanged , setDataChanged] = useState(false)
useEffect(()=>{
// Rest of your logic here
} , [dataChanged])
useEffect(()=>{
// Your logic will run only one time
// on Success we change the dataChanged state so the other useEffect will
// run basically you can run the rest of your logic in the other
// useEffect so the infinite loop won't happen
// setDataChanged( (prev) => !prev )
} , [])
Was able to use map method that worked
personService
.update(updatedPerson.id, newPerson)
.then(res => {
setPersons(persons.map(p => p.id !== updatedPerson.id ? p : res))
})
Faced a problem with the navigate() in Gatsy function, using the navigate function, you can pass an object as a second parameter to another page. I did as in the documentation, but when getting data I get undefined.
const Panel = () => {
const [name, setName] = useState('')
useEffect (() => {
if (name !== undefined) {
setName('Hello World')
}
}, [name])
const handleRedirect = () => {
navigate('/cabinet/', { state: { name }})
}
return (
<div>
<button onClick={handleRedirect}>Redirect</button>
<div>
)
}
const Cabinet = ({location}) => {
console.log(location.state.name) // undefined
return (
<div>
<h1>{location.state.name}</h1>
</div>
)
}
Your problem is a matter of timings/asynchronously. Your navigate built-in function looks good, however, you are setting the name (with your setName setter) after the navigation occurs. In addition, you are missing the key for the state. Try something like:
const handleRedirect = () => {
if(name) navigate('/cabinet', {state: {'name': name}}); // Try hardcoding Hello World for debugging purposes {'name'; 'Hello World'}
}
Then in your Cabinet component:
const Cabinet = ({location}) => {
console.log(location.state.name) // will be your Hello World
return (
<div>
<h1>{location.state.name}</h1>
</div>
)
}
Alternatively, you can do an async/await approach, something like:
const setNameFunction = ()=>{
setName('Hello World')
return name
}
const handleRedirect = async () => {
let nameFromFunction= await setNameFunction;
navigate('/cabinet/', { state: { 'name': nameFromFunction }}) // use if(nameFromFunction) navigate('/cabinet/', { state: { name: nameFromFunction }}) alternatively
}
This second approach will make the ecosystem dynamic, allowing you to pass a custom name based on some logic that you will need to add (passing the name as a parameter through those functions).
Alternatively, you can look for your state in your Cabinet component using window object in window.history.state.
I have to get a users from backend and push the result array into a property of third-party component. I can't push the result into the component's state, like setUsersList, because it leads to infinity render-loop. So I decided to use a function which will put the data from backend into a variable and return variable's value.
<InputAutocomplete
...
value={selectedUserName}
onChange={(value: string) => setSelectedUserName(value)}
options={getFilteredUsers(selectedUserName)}
/>
My function is:
const getFilteredUsers = (typedValue: any): any[] => {
console.log('***getting into getFilteredUsers method')
if (!typedValue) {
return []
}
let usersList: any[]
repository.GetUsers(typedValue).then((response) => {
if (response.StatusCode !== null) {
response.Message.then((errorText) => showNotificationPopup(errorText, consts.ERROR_OCCURED, 'error'))
return
}
response.Data.then((users) => {
console.log('filling usersList')
usersList = (users.map((u) => {
return {key: u.Id, value: `${u.Name} - ${u.Position} - ${u.DopOfficeName}` }
})
)
})
})
console.log('leaving function')
return usersList
}
It works unacceptable for me (although it works as expected):
***getting into getFilteredUsers method
leaving function (with empty array)
filling usersList (too late)
The property "options" of third's party component doesn't accept Promise<any[]>, it can only accept any[], thus I can't mark my function as async and await the result from repository before to leave the function.
How to prevent leaving function before I'll get the result?
Maybe I've made a wrong decision at all. If I'm wrong I need a help, how to implement server-side refilling of autocomplete input component.
I can't push the result into the component's state, like setUsersList, because it leads to infinity render-loop
This should not be the case.
const SomeComponent = () => {
const [options, setOptions] = React.useState([]);
const [username, setUsername] = React.useState([]);
React.useEffect(() => {
if (!typedValue) return [];
repository.GetUsers(typedValue).then((response) => {
if (response.StatusCode !== null) {
response.Message.then((errorText) => showNotificationPopup(errorText, consts.ERROR_OCCURED, 'error'));
return;
}
response.Data.then((users) => {
console.log('filling usersList');
const usersList = users.map(u => {
return { key: u.Id, value: `${u.Name} - ${u.Position} - ${u.DopOfficeName}`};
});
setOptions(usersList);
})
})
console.log('leaving function')
}, [username]);
return (
<InputAutocomplete
value={username}
onChange={(value) => setUsername(value)}
options={options}
/>
);
};
Above code should not go in infinite loop.