I put together a working example, stubbing out your socket calls with a simple setTimeout
.
Probably the most interesting part is where I moved the sendChatAction
function into the component so it could use the dispatch
method of the reducer:
export default function Store(props) {
const [allChats, dispatch] = React.useReducer(reducer, initState);
function sendChatAction(value) {
dispatch({
type: "RECEIVE_MESSAGE",
payload: value
});
}
...
}
Everything appears to work. Compare your code to this:
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>
);
}
CLICK HERE to find out more related problems solutions.