#javascript #reactjs #websocket
#javascript #reactjs #websocket
Вопрос:
Я пытаюсь заставить компонент react отображать список элементов, которые были извлечены из серверной части с помощью websockets. однако, поскольку мой websockets.js файл находится за пределами иерархии react, список фактически никогда не обновляется, и поэтому элементы не отображаются.
До сих пор я пытался добавить общедоступный массив «проектов» в websocketservice.js файл, а затем ссылаться на этот список внутри моего компонента react. однако он никогда не обновляется, даже когда websocket возвращается со своими собранными значениями.
websocketservice.js
class WebSocketService{
projects = []
static getInstance() {
if(!WebSocketService.instance){
WebSocketService.instance = new WebSocketService();
}
return WebSocketService.instance;
}
constructor() {
this.socketRef = null;
}
connect() {
const path = 'ws://127.0.0.1:8000/ws/wspingpong/';
this.socketRef = new WebSocket(path);
this.socketRef.onopen = () => {
console.log('websocket opened');
this.sendTestMessage();
};
this.socketRef.onmessage = e => {
console.log("message recieved: " e.data);
this.messageRecieved(e.data)
}
this.socketRef.onerror = e => {
console.log(e.onmessage);
}
this.socketRef.onclose = () => {
console.log('websocket is closed');
this.connect();
}
}
sendMessage(data){
try{
this.socketRef.send(JSON.stringify(...data))
}catch(err){
console.log(err.message)
}
}
messageRecieved(data) {
const parsedData = JSON.parse(data);
const command = parsedData.command;
if(command === "STC-test"){
console.log("succesfully recieved a testmessage from the server");
}
if(command === "STC-login"){
console.log("succesfully logged in, redirecting...")
console.log(parsedData.message)
this.loggedUser = {
username: parsedData.message["username"],
email: parsedData.message["email"]
}
history.push("/project")
}
if(command === "STC-getprojects"){
console.log(parsedData.message)
this.projects = parsedData.message
}
}
выше console.log(parsedData.message)
правильно показан список проектов
getProjects(){
console.log("getting projects....")
if(this.loggedUser != null){
var dict = [];
dict.push({
command: "CTS-getprojects",
message: ""
})
this.sendMessage(dict)
}else{
console.log("no user logged in, unable to retrieve projects.")
}
}
ProjectSelect.tsx
export default class ProjectSelect extends React.Component<Iprops>{
constructor(props: Iprops) {
super(props)
}
render() {
return (
<div>
{
WebsocketInstance.loggedUser != null ?
<div>
<h3>ProjectSelect, welcome user: {WebsocketInstance.loggedUser.username}</h3>
{WebsocketInstance.projects.map((name: any) => <span>name: {name}</span>)}
<label>Project Name:</label>
<input onChange={e => this.setState({ projectname: e.target.value })}></input>
<button onClick={() => this.createProject()}>Create a new project</button>
</div>
:
null
}
</div>
)
}
createProject() {
WebsocketInstance.createProject(this.state.projectname)
}
РЕДАКТИРОВАТЬ
попытка исправления:
constructor(props: Iprops) {
super(props)
this.state = {
projectnames: []
}
this.setState({
projectnames: WebsocketInstance.projects
})
}
а затем внутри метода рендеринга я добавил:
{this.state.projectnames.map((name: any) => <span>name: {name}</span>)}
однако это не сработало
Моя цель состоит в том, чтобы, как только websocket обновляет список собранными значениями, компонент react обновляется и отображает список значений
Ответ №1:
Я бы взял возврат из WebsocketInstance.createProject(this.state.projectname)
и сделал что-то вроде
const myData = WebsocketInstance.createProject(this.state.projectname);
this.setState({webSocitReturn: myData})
Шаблон заключается в обновлении состояния / реквизита для его обновления. Постарайтесь максимально использовать API react, чтобы получить желаемый результат.
Кроме того, имейте в виду, что приведенный выше пример кода, который я привел, нуждается в некоторой доработке для учета асинхронности.
РЕДАКТИРОВАТЬ: если это не сработает, вот сообщение в блоге о сокетах и реакции. https://blog.logrocket.com/beyond-rest-using-websockets-for-two-way-communication-in-your-react-app-884eff6655f5
Комментарии:
1. я попытался сделать что-то похожее (не хочу, чтобы createproject отвечал за обновление списка), установив массив в состоянии websocketinstance.projects var . и вызов state.projectnames в моем методе рендеринга, это, однако, также не сработало
Ответ №2:
Мне удалось решить эту проблему, предоставив моему websocketinstance метод для обратного вызова:
projectselect.tsx
constructor(props: Iprops) {
super(props)
this.state = {
projectnames: [],
projectname: ""
}
WebsocketInstance.callback = this.getProjectsCallback
WebsocketInstance.getProjects()
}
renderMessages = (messages:any) => {
return messages.map((message:any) => (
<div>
<span>{message["projectname"]}</span>
<button onClick={() => this.openProject(message["id"])}>Load project</button>
</div>
))
}
getProjectsCallback = (messages:any) => {
this.setState({
projectnames: messages
})
}
когда обратный вызов вызывается из websocket и получает новый список, я обновляю состояние, и react автоматически перенаправляет компонент.
websocket.js
messageRecieved(data) {
const parsedData = JSON.parse(data);
const command = parsedData.command;
if(command === "STC-test"){
console.log("succesfully recieved a testmessage from the server");
}
if(command === "STC-login"){
console.log("succesfully logged in, redirecting...")
console.log(parsedData.message)
this.loggedUser = {
username: parsedData.message["username"],
email: parsedData.message["email"]
}
history.push("/project")
}
if(command === "STC-getprojects"){
console.log(parsedData.message)
this.callback(parsedData.message)
}
}