in general, I have a problem. I want to send a message to an endpoint, in which to send all messages from the database to the web socket topic so that the client can receive them all. Here is my method for sending a message (I use MONGODB):
@PostMapping("/send")
public void sendMessage(@RequestBody Message message){
mongoTemplate.insert(message);
simpMessagingTemplate.convertAndSend("/topic/greetings", mongoTemplate.query(Message.class).as(Message.class).all());
}
In general, nothing out of the ordinary, There are no problems with this, everything goes to the topic, everything is cool. Now all this needs to be caught on the client, for this I wrote the following code:
var stompClient: any = null;
const SockJS = require('sockjs-client')
const Stomp = require('stompjs')
function App() {
const [posts, setPosts]: any = useState("")
const [handleInput, setHandleInput] = useState("")
function setValueInput(e: any){
setHandleInput(e.target.value);
}
function sendMessage(e: any){
e.preventDefault();
axios.post('http://localhost:8080/api/send', {
id: Date.now(),
text: handleInput,
})
.then(function (response) {
setHandleInput("");
})
.catch(function (error) {
console.log(error);
});
}
useEffect(function (){
let Sock = new SockJS('http://localhost:8080/ws')
stompClient= Stomp.over(Sock)
stompClient.debug = null
setTimeout(function() {
stompClient.connect({},function (frame: any){
console.log(`Connected: ${frame}`)
stompClient.subscribe("/topic/greetings", function (greeting: any) {
console.log(greeting.body)
const tempParsedJson = JSON.stringify(greeting.body)
console.log(tempParsedJson)
setPosts(tempParsedJson)
});
}, 1200)})
}, [])
return (
<div className="App">
{posts && posts.map((data: any) => (
<Form key={data.id} {...data}/>
))}
<div className={style.dop}>
<input onChange={setValueInput} value={handleInput} className={style.input}/>
<a onClick={sendMessage} className={style.button}>Submit</a>
</div>
</div>
);
}
export default App;
In short, we successfully subscribe to the topic, everything is fine again, but until I need to get the objects. In the console I see this:
The first is what I get from Java, the second is what I’m trying to translate into JSON. After sending messages, according to my code, we see that I am trying to display this data separately in each element, but when I try I get an error:
ERROR
posts.map is not a function
I hope you can help, I don’t know what to do. By the way, I tried to translate List in Java through gson to json, but the same error is displayed ..
Help please..
My Form:
const Form: React.FC<Props> = ({id, text}) => {
function handleDelete(e: any) {
e.preventDefault();
axios.post('http://localhost:8080/api/deleteMessage', {
id: id,
text: text,
})
.then(function (response) {
})
.catch(function (error) {
console.log(error);
});
}
return (
<div>
<div className={style.wrapper}>
<div className={style.coontainer}>
<div className={style.form}>
<p>{text}</p>
<a onClick={handleDelete} style={{display: "inline", margin: "0 20px", color: "white", cursor: "pointer"}}>Удалить</a>
</div>
</div>
</div>
</div>
);
};
export default Form;
3
Answers
Because your code is async and not immediately fetched and might take time, until the data is fetched your data is undefined, and the posts are empty string until the data is fetched, then it’ll update to be replaced with data
therefore this error was thrown to your console, because the String instance has no method named "map"
so to solve this problem you have to solutions.
the first one is to add the ternary operator (?) after the word posts, to be like that posts?.map() this will make the code only work if the data exists and it has a map method (in other words, if the data is an array)
or the second solution is defining the initial state value an empty array
First make initial state an empty array like this piece of code:
And then, use ternary operator while using map function, like:
The other answers have picked up on the fact that you need
posts
to be initialised to be an array when you declare it using theuseState
hook in the following line:but they seem to have missed the problem in this piece of code:
greeting.body
looked good to me: according to your firstconsole.log
output, it looked to be an array containing some objects, and you could certainly.map
over that if that were the case. However, on further inspection, if it actually were parsed JSON data, your browser would display that differently. In particular, you’d be able to expand parts of the JSON data.However, in the line below that, you’re not helping yourself.
You take the data you have in
greeting.body
, and then you stringify it. You then name your variable to give yourself the impression you’ve ‘parsed’ JSON, which is unhelpful. You haven’t parsed JSON here, in fact, you’ve done the opposite. Parsing is the process of converting a string of JSON into JavaScript objects and arrays, stringifying is converting the other way.Therefore, the fix is not to call
JSON.stringify
ongreeting.body
, but to callJSON.parse
on it instead.Your Java back-end may have send JSON over the network to your front-end. I would expect that Axios would have parsed this JSON into JavaScript objects for you, so you can start using them straight away, but for reasons I’m unsure about, this seems not to be the case.
Note that I don’t have your back-end and haven’t run this code, so there could be other problems. I am only explaining how to fix the "posts.map is not a function" error.