【问题标题】:React Store not updating context with React.useReducerReact Store 不使用 React.useReducer 更新上下文
【发布时间】:2021-02-18 18:14:21
【问题描述】:

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

我认为 allChats 将使用 Store 中的文本字段值进行更新,然后使用 React.useContext(CTX) 在 Dashboard.js 中访问。 allChats 仅记录存储中的 initState。

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

仪表板.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')
});

【问题讨论】:

    标签: reactjs websocket store dispatch use-reducer


    【解决方案1】:

    我整理了一个working example,用一个简单的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>
      );
    }
    

    仪表板.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>
      );
    }
    

    【讨论】:

      猜你喜欢
      • 2017-10-20
      • 2022-01-09
      • 2018-09-30
      • 1970-01-01
      • 2021-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多