React Store未使用React.useReducer更新上下文



我正在用React构建一个简单的聊天室,希望能够通过在文本字段中键入并按下发送按钮来用文本更新聊天窗口。我原以为,当保存上下文的Store调用React.UseReducer时,它会用传递到Dashboard组件的文本值更新上下文。当我在reducer中记录文本时,它会显示应该添加到聊天窗口的文本字段中的值。但是,在运行useReducer之后,状态仍然只是initState,并且不包括发送的文本。

我以为allChats会用Store中的文本字段值更新,然后使用React.useContext(CTX(在Dashboard.js中访问。allChats只作为Store中的initState进行日志记录。

如何更新以将文本传递到上下文中,以便聊天窗口显示它?

Dashboard.js


export default function Dashboard() {
const classes = useStyles();
//CTX store
const {allChats, sendChatAction, user} = React.useContext(CTX);
const topics = Object.keys(allChats);
//local state
const [activeTopic, changeActiveTopic] = React.useState(topics[0])
const [textValue, changeTextValue] = React.useState('')

return (
<div className={classes.root}>
<Paper className={classes.root}>
<Typography variant="h4" component="h4">
Chat Room
</Typography>
<Typography variant="h5" component="h5">
{activeTopic}
</Typography>
<div className={classes.flex}>
<div className={classes.topicsWindow}>
<List>
{topics.map((topic) => (
<ListItem onClick={e=> changeActiveTopic(e.target.innerText)} key={topic} button>
<ListItemText primary={topic} />
</ListItem>
))}
</List>
</div>
<div className={classes.chatWindow}>
{ 
allChats[activeTopic].map((chat, i) => (
<div className={classes.flex} key={i}>
<Chip label={chat.from} className={classes.chip} />
<Typography variant='body1' gutterBottom>{chat.msg}</Typography>
</div>
))
}
</div>
</div>
<div className={classes.flex}>
<TextField
label="Send a Chat"
className={classes.chatBox}
value={textValue}
onChange={e => changeTextValue(e.target.value)}
/>
<Button
variant="contained"
color="primary"
className = {classes.button}
onClick ={() => {
sendChatAction({from: user, msg: textValue, topic: activeTopic});
changeTextValue('');
}}
>
Send
</Button>
</div>
</Paper>
</div>
);
};

存储


import React from 'react'
import io from 'socket.io-client'
export const CTX = React.createContext()

const initState = {
general: [
{from:'matt',msg:'yo'},
{from:'jeff',msg:'yo'},
{from:'steve',msg:'yo'},
],
topic2:[
{from:'bill',msg:'yo'},
{from:'joe',msg:'yo'},
{from:'dave',msg:'yo'},
]
}
function reducer(state, action){
//payload logs as:
//{from:'from',msg:'msg',topic:'topic'}
const {from, msg, topic} = action.payload;
switch(action.type){
case 'RECEIVE_MESSAGE':
return {
...state,
[topic]:[
...state[topic],
{from,msg}
]
}
default:
return state
}
}
let socket;
function sendChatAction(value){
socket.emit('chat message', value);
}
export default function Store(props){
const [allChats,dispatch] = React.useReducer(reducer, initState);
if(!socket){
socket = io(':3001');
socket.on('chat message', function(msg){
//msg logs with text field value that we need in 
//chatWindow on Dashboard.
dispatch({type:'RECEIVE_MESSAGE', payload: msg});
});
}
//allChats logs only as initState, above.
const user = 'matt' + Math.random(100).toFixed(2);
return (
<CTX.Provider value={{allChats,sendChatAction,user}}>
{props.children}
</CTX.Provider>
)
}

index.js

var app = require('express')()
var http = require('http').createServer(app);
var io = require('socket.io')(http)
app.get('/',function(req,res){
res.send('<h1>hello world</h1>')
});
io.on('connection',function(socket){
console.log('a user connected',socket);
socket.on('chat message',function(msg){
console.log('message: ' + JSON.stringify(msg))
io.emit('chat message', msg)
});
})
http.listen(3001,function(){
console.log('listening on *:3001')
});

我整理了一个工作示例,用一个简单的setTimeout截断套接字调用。

可能最有趣的部分是我将sendChatAction函数移动到组件中,这样它就可以使用reducer的dispatch方法:

export default function Store(props) {
const [allChats, dispatch] = React.useReducer(reducer, initState);
function sendChatAction(value) {
dispatch({
type: "RECEIVE_MESSAGE",
payload: value
});
}
...
}

一切似乎都正常。将您的代码与此进行比较:

Store.jsx

import React, { useEffect } from "react";
export const CTX = React.createContext();
const initState = {
general: [
{ from: "matt", msg: "yo" },
{ from: "jeff", msg: "yo" },
{ from: "steve", msg: "yo" }
],
topic2: [
{ from: "bill", msg: "yo" },
{ from: "joe", msg: "yo" },
{ from: "dave", msg: "yo" }
]
};
function reducer(state, action) {
//payload logs as:
//{from:'from',msg:'msg',topic:'topic'}
const { from, msg, topic } = action.payload;
switch (action.type) {
case "RECEIVE_MESSAGE":
return {
...state,
[topic]: [...state[topic], { from, msg }]
};
default:
return state;
}
}
export default function Store(props) {
const [allChats, dispatch] = React.useReducer(reducer, initState);
function sendChatAction(value) {
dispatch({
type: "RECEIVE_MESSAGE",
payload: value
});
}
useEffect(() => {
//msg logs with text field value that we need in
//chatWindow on Dashboard.
setTimeout(() => {
sendChatAction({ from: "matt", msg: "hey", topic: "general" });
}, 3000);
}, []);
//allChats logs only as initState, above.
const user = "matt" + Math.random(100).toFixed(2);
return (
<CTX.Provider value={{ allChats, sendChatAction, user }}>
{props.children}
</CTX.Provider>
);
}

Dashboard.jsx

import {
Button,
Chip,
List,
ListItem,
ListItemText,
Paper,
TextField,
Typography
} from "@material-ui/core";
import React from "react";
import { CTX } from "./Store";
export default function Dashboard() {
const classes = {};
//CTX store
const { allChats, sendChatAction, user } = React.useContext(CTX);
const topics = Object.keys(allChats);
//local state
const [activeTopic, changeActiveTopic] = React.useState(topics[0]);
const [textValue, changeTextValue] = React.useState("");
return (
<div className={classes.root}>
<Paper className={classes.root}>
<Typography variant="h4" component="h4">
Chat Room
</Typography>
<Typography variant="h5" component="h5">
{activeTopic}
</Typography>
<div className={classes.flex}>
<div className={classes.topicsWindow}>
<List>
{topics.map((topic) => (
<ListItem
onClick={(e) => changeActiveTopic(e.target.innerText)}
key={topic}
button
>
<ListItemText primary={topic} />
</ListItem>
))}
</List>
</div>
<div className={classes.chatWindow}>
{allChats[activeTopic].map((chat, i) => (
<div className={classes.flex} key={i}>
<Chip label={chat.from} className={classes.chip} />
<Typography variant="body1" gutterBottom>
{chat.msg}
</Typography>
</div>
))}
</div>
</div>
<div className={classes.flex}>
<TextField
label="Send a Chat"
className={classes.chatBox}
value={textValue}
onChange={(e) => changeTextValue(e.target.value)}
/>
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={() => {
sendChatAction({
from: user,
msg: textValue,
topic: activeTopic
});
changeTextValue("");
}}
>
Send
</Button>
</div>
</Paper>
</div>
);
}

App.js

import React from "react";
import Dashboard from "./Dashboard";
import Store from "./Store";
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Store>
<Dashboard />
</Store>
</div>
);
}

最新更新