I am trying to emit a data to client via socket.io in next.js setup.But i dont get any data via it.
Below is my server.js code
const app = require('express')()
const server = require('http').Server(app)
const io = require('socket.io')(server)
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const nextApp = next({ dev })
const nextHandler = nextApp.getRequestHandler()
// socket.io server
io.on('connection', socket => {
console.log('a user is connected')
socket.broadcast.emit('now', {
message: 'Hello'
})
socket.on('disconnect', () => {
console.log('user disconnected')
})
})
nextApp.prepare().then(() => {
app.get('*', (req, res) => {
return nextHandler(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
when a user is connected it console logs a user is connected its working fine.
into my _app.js file i have included socket.io as instructed in next.js example: https://github.com/zeit/next.js/blob/canary/examples/with-socket.io/pages/_app.js
Here is my _app.js file
import App from 'next/app'
import React from 'react'
import io from 'socket.io-client'
class MyApp extends App {
static async getInitialProps ({ Component, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
return { pageProps }
}
state = {
socket: null
}
componentDidMount () {
// connect to WS server and listen event
const socket = io()
this.setState({ socket })
}
// close socket connection
componentWillUnmount () {
this.state.socket.close()
}
render () {
const { Component, pageProps } = this.props
return <Component {...pageProps} socket={this.state.socket} />
}
}
export default MyApp
Main problem is In index.js file.Below is that file
import { Component } from 'react'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'
class ChatOne extends Component {
// init state with the prefetched messages
state = {
message: ''
}
componentDidMount () {
this.props.socket.on('now', data => {
this.setState({
message: data.message
})
})
}
render () {
return (
<div>
{this.state.message}
</div>
)
}
}
export default ChatOne
And after launch i am getting this error.I am asking this question to about the error of this question
Your first render of <App /> won't have this.state.socket as that isn't set until it has mounted.
render () {
const { socket } = this.state;
if(!socket) {
return <div>Loading...</div>
}
const { Component, pageProps } = this.props
return <Component {...pageProps} socket={socket} />
}
Related
I am trying to use the onAuthStateChanged trigger to unsubscribe but I keep getting:
Uncaught TypeError: (0 , firebase_util__WEBPACK_IMPORTED_MODULE_0_.getModularInstance)(...).onAuthStateChanged is not a function
below is my Authcontext.js
import React ,{useEffect, useState ,useContext} from 'react';
import { auth } from '../api/firebase';
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [loading,setLoading] = useState(true);
function register (email,password) {
return auth.createUserWithEmailAndPassword(email,password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user);
});
return unsubscribe;
}, []);
const value = {
currentUser,
register,
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
You are not subscribing to the StateChange correctly , try the following
React.useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => { // detaching the listener
if (user) {
// ...your code to handle authenticated users.
} else {
// No user is signed in...code to handle unauthenticated users.
}
});
return () => unsubscribe(); // unsubscribing from the listener when the component is unmounting.
}, []);
I have faced the same error when I was working on react native app and because of firebase version change they have update some methods.
I have resolved this by below code and firebase verison is ^9.6.11.
you do not need to pull onAuthStateChanged
import { getAuth } from "firebase/auth";
export const yourfunc = () => {
const auth = getAuth();
auth.onAuthStateChanged((user) => {
console.log(user)
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
So, I am attempting a simple, single-page application on the MERN stack that takes notes - a note title and a note content and displays them on the same root route; the data is supposed to also be saved on the backend for later retrieval. At this time, there is no authentication. My backend seems to be working perfectly, but when I connect the front end React application to the backend MongoDB database, my GET request (using axios instance) fails.
My backend renders on localhost:5000, no problem.
But on localhost:3000, I'm seeing this error in App.jsx:
Error: Request failed with status code 404
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:62)
Here's what I have going on:
BACKEND
// server.js
import express from 'express';
import cors from 'cors';
import notes from './api/notes.route.js';
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/api/v1/notes', notes);
app.use('*', (req, res) => res.status(404).json({ error: 'not found' }));
export default app;
// notes.route.js
import express from 'express';
import NotesController from './notes.controller.js';
const router = express.Router();
router
.route('/')
.get(NotesController.apiGetNotes)
.post(NotesController.apiPostNote)
.put(NotesController.apiUpdateNote)
.delete(NotesController.apiDeleteNote);
export default router;
// notes.controller.js
import NotesDAO from '../dao/notesDAO.js';
class NotesController {
static apiGetNotes = async (req, res, next) => {
const notesPerPage = req.query.notesPerPage
? parseInt(req.query.notesPerPage)
: 20;
const page = req.query.page ? parseInt(req.query.page) : 0;
const { notesList, totalNumNotes } = await NotesDAO.getNotes({
page,
notesPerPage
});
let response = {
notesList,
page,
notesPerPage,
totalNumNotes
};
res.json(response);
};
export default NotesController;
// notesDAO.js
import mongodb from 'mongodb';
const ObjectID = mongodb.ObjectId;
let notes;
class NotesDAO {
// call this on db connection:
static injectDB = async conn => {
if (notes) return;
try {
notes = await conn.db(process.env.NOTESDB_NS).collection('notes');
} catch (e) {
console.error(`unable to establish collection handle in notesDAO: ${e}`);
}
};
static getNotes = async ({ page = 0, notesPerPage = 20 } = {}) => {
let query;
let cursor;
try {
cursor = await notes.find(query);
} catch (e) {
console.error(`unable to issue find command, ${e}`);
return { notesList: [], totalNumNotes: 0 };
}
const displayCursor = cursor.limit(notesPerPage).skip(notesPerPage * page);
try {
const notesList = await displayCursor.toArray();
const totalNumNotes = await notes.countDocuments(query);
return { notesList, totalNumNotes };
} catch (e) {
console.error(
`unable to convert cursor to array or problem counting documents, ${e}`
);
return { notesList: [], totalNumNotes: 0 };
}
};
}
export default NotesDAO;
FRONTEND
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Route path="/" component={App} />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
// App.jsx
import { useState, useEffect } from 'react';
import NoteDataService from './services/note.js';
import Header from './Header';
import Footer from './Footer';
import CreateArea from './CreateArea';
import Notes from './Notes';
const App = () => {
const [notes, setNotes] = useState([]);
useEffect(() => {
retrieveNotes();
}, []);
const retrieveNotes = async () => {
await NoteDataService.getAll()
.then(response => {
console.log(response.data);
setNotes(response.data.notes);
})
.catch(e => console.log(e));
};
return (
<>
<Header />
<CreateArea clicked={addNote} />
<Notes notes={notes} clicked={deleteNote} />
<Footer />
</>
);
};
export default App;
// http-common.js
import axios from 'axios';
export default axios.create({
baseURL: 'http://localhost:5000/api/vi/notes',
headers: {
'Content-type': 'application/json'
}
});
// note.js
import http from '../http-common.js';
class NoteDataService {
getAll() {
return http.get('/');
}
}
export default new NoteDataService();
You have a typo. The backend route is /api/v1/notes, but the frontend is sending requests to /api/vi/notes
I have made an API in Node and can see the data using Postman and by hiting the endpoint in the browser. But, when I try to fetch it using React, I can't see it and am getting few errors. one of them is related to the CORS, which I tried to solve by adding a proxy in the package.json of the client folder but it's still not getting solved. Here's my code:
Error:
img
App.js
import React, { useState, useEffect } from "react";
import "./App.css";
//Pages
//components
import Navbar from "./Components/Navbar";
function App() {
let initialData = [];
const [data, setData] = useState(initialData);
const [isFetched, setIsFetched] = useState(false);
useEffect(() => {
let headers = new Headers({
"Content-Type": "application/json",
Accept: "application/json",
});
fetch(`http://localhost:4000/`, headers)
.then((res) => res.json())
.then(
(result) => {
setData(result);
setIsFetched(true)
console.log("result"+result)
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
console.log(error);
}
);
});
return (
<div className="container">
<Navbar data={data} isFetched={isFetched} />
</div>
);
}
export default App;
NodeJs code:
const router = require('express').Router();
router.route('/').get((req, res) => {
let displayData=[{
user:'a',
id:1
},
{
user:'b',
id:2
}
]
res.status(200).json(displayData);
res.send("<h1>Home</h1>")
})
module.exports = router
server.js:
const express = require('express');
const cors = require('cors');
const { pool } = require("./db/dbConfig")
const app = express();
const port = process.env.PORT || 4000;
const homeRouter = require("./routes/home")
//middleware
app.use("/", homeRouter);
app.use(cors());
app.use(express.json());
app.listen(port, () => {
console.log(`We are live on port: ${port}`);
});
I am connecting nodejs backend with reactjs using redux. But every time i submit login form I get an error something like this.
TypeError: Cannot read property 'then' of undefined
and I have no idea where this is coming from. I tried to find this on StackOverflow I did find something that said I need to convert then as a function but still I am getting the same error.
This is my code
loginPage.js
class LoginPage extends Component {
submit = (data) => this.props.login(data).then(() => this.props.history.push('/'));
render() {
return (
<div>
<h1>LoginPage</h1>
<LoginForm submit={this.submit}/>
</div>
);
}
}
loginform.js
onSubmit = (e) => {
e.preventDefault();
const errors = this.validate(this.state.data);
this.setState({ errors });
if (Object.keys(errors).length === 0) {
this.setState({ loading: true});
this.props
.submit(this.state.data)
.catch(err => this.setState({ errors: err.response.data.errors, loading: false }));
}
};
api.js
import axios from 'axios';
export default {
user: {
login: (credentials) =>
axios.post('/api/auth', { credentials })
.then(res => res.data.user),
}
}
actions/auth.js
import { USER_LOGGED_IN } from '../types';
import api from '../api';
export const userLoggedIn = (user) => ({
type: USER_LOGGED_IN,
user
})
export const login = (credentials) => (dispatch) => {
api.user.login(credentials)
.then(user => {
dispatch(userLoggedIn(user))
});
}
backend/routes/auth.js
import express from 'express';
import User from '../Models/User';
const router = express.Router();
router.post('/', (req, res) => {
const { credentails } = req.body;
User.findOne({email: req.body.email}).then(user => {
if (user.email) {
res.json({success: true})
} else {
res.status(400).json({errors: {global: "Invalid Credentials"}})
}
})
})
loginForm.js
import React, {Component} from 'react';
import LoginForm from '../Forms/LoginForm';
import PropTypes from 'prop-types'
import { connect } from 'react-redux';
import { login } from '../../actions/auth';
class LoginPage extends Component {
submit = (data) => this.props.login(data).then(() => this.props.history.push('/'));
render() {
return (
<div>
<h1>LoginPage</h1>
<LoginForm submit={this.submit}/>
</div>
);
}
}
LoginPage.propTypes = {
history: PropTypes.shape({
push: PropTypes.func.isRequired
}).isRequired,
login: PropTypes.func.isRequired
};
export default connect(null, {login})(LoginPage);
You have not specified any Promise to the login function. Thus, you are getting a undefined error.
You can also do this by two ways:
1. Promise
api.user.login(credentials)
.then(user => {
dispatch(userLoggedIn(user))
});
Api.js
import axios from 'axios';
export default {
user: {
login: new Promise(credentials, resolve, reject) =>
axios.post('/api/auth', { credentials })
.then(res => resolve(res.data.user))),
}
}
2. callback function.
actions/auth.js
api.user.login(credentials,
(user => {
dispatch(userLoggedIn(user))
})
);
Api.js
import axios from 'axios';
export default {
user: {
login: (credentials, callback) =>
axios.post('/api/auth', { credentials })
.then(res => callback(res.data.user)),
}
}
I'm trying to render the following component on the server side as part of a universal/isomorphic app:
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { GridLoader } from 'halogen';
import PostListItem from '../../components/PostListItem/PostListItem';
import { primary as color } from '../../colors';
import { changeSelectedPost, deletePostRequest } from '../../redux/modules/post';
export default connect()(class PostListView extends Component {
static propTypes = {
posts: ImmutablePropTypes.listOf(ImmutablePropTypes.contains({
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
content: PropTypes.string.isRequired,
slug: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
})).isRequired,
loading: PropTypes.boolean.isRequired,
dispatch: PropTypes.func.isRequired,
}
handleClick(post) {
this.props.dispatch(changeSelectedPost(post));
}
handleDelete(post) {
if (confirm('Do you want to delete this post')) { // eslint-disable-line
this.props.dispatch(deletePostRequest(post));
}
}
render() {
if (typeof window !== 'undefined' && this.props.loading) {
return (
<div className="container">
<GridLoader color={color} />
</div>
);
}
return (
<div className="listView">
{
this.props.posts.toSeq().map((post, i, arr) => (
<PostListItem
post={post}
key={i}
onClick={this.handleClick.bind(this, post)}
onDelete={this.handleDelete.bind(this, post)}
/>
))
}
</div>
);
}
});
but I receive the error:
module.exports = document.createElement('div').style;
^
ReferenceError: document is not defined
Adding the if block seems to have tripped up the app (the app rendered perfectly before, both server and client side). I'm most likely missing something quite obvious, knowing my track record :P. Any suggestions? :)
UPDATE: File that handles server-side rendering and the rest of the app on the server side:
// server/server.js
'use-strict';
import path from 'path';
import bodyParser from 'body-parser';
import Express from 'express';
import React from 'react';
import webpack from 'webpack';
import { fromJS } from 'immutable';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import { match, RouterContext } from 'react-router';
import { Model } from 'objection';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import config from '../webpack.config.dev';
import routes from '../shared/routes';
import configureStore from '../shared/redux/configureStore';
import assets from './assets';
import db from './db';
import posts from './routes/post.routes';
import Post from './models/post';
import serverConfig from './serverConfig';
// Initialize the Express App
const app = new Express();
Model.knex(db);
if (process.env.NODE_ENV !== 'production') {
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
}
// Apply body Parser and server public assets and routes
app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }));
if (process.env.NODE_ENV !== 'production') {
app.use(Express.static(path.resolve(__dirname, '../static')));
}
function getFilename() {
if (process.env.NODE_ENV !== 'production') {
return '"/dist/bundle.js"';
}
return `"/dist/${assets}"`;
}
app.use('/api', posts);
// Render Initial HTML
const renderFullPage = (html, initState, jsFile) => {
return `
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/styles/normalize.css">
<link rel="stylesheet" href="/styles/skeleton.css">
<title>CeresShop</title>
</head>
<div id="root">${html}</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initState)};
</script>
<script src=${jsFile}></script>
</body>
</html>
`;
};
// Server Side Rendering based on routes matched by React-router.
app.use((req, res) => {
match({ routes, location: req.url }, (err, redirectLocation, renderProps) => {
if (err) {
return res.status(500).end('Internal server error');
}
if (!renderProps) {
return res.status(404).end('Not found!');
}
const initialState = fromJS({
postReducer: {
posts: [],
post: {},
loading: false,
},
route: {
locationBeforeTransitions: null,
},
});
async function loadData() {
if (req.url.includes('post')) {
try {
const newSlug = req.url.substring(5).split('-');
const newId = newSlug[newSlug.length - 1];
const newPost = await Post.query().where('id', newId);
const toBeProcessed = JSON.stringify(newPost[0]);
return initialState.setIn(['postReducer', 'post'], fromJS(JSON.parse(toBeProcessed)));
} catch (error) {
console.log(error);
return initialState;
}
}
try {
const newPosts = await Post.query();
newPosts.sort((a, b) => b.dateadded - a.dateadded);
const toBeProcessed = JSON.stringify(newPosts);
return initialState.setIn(['postReducer', 'posts'], fromJS(JSON.parse(toBeProcessed)));
} catch (error) {
console.log(error);
return initialState;
}
}
loadData().then((currentState) => {
const store = configureStore(currentState);
const createElement = (Component, props) => (
<Component
{...props}
radiumConfig={{ userAgent: req.headers['user-agent'] }}
/>
);
const initialView = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} createElement={createElement} />
</Provider>
);
const finalState = store.getState().toJS();
res.status(200).end(renderFullPage(initialView, finalState, getFilename()));
}).catch(err1 => {
console.log(err1);
res.end(renderFullPage(`Error: ${err1}`, {}, getFilename()));
});
});
});
// start app
app.listen(serverConfig.port, (error) => {
if (!error) {
console.log(`DAT SERVER is running on port: ${serverConfig.port}!`); // eslint-disable-line
}
});
export default app;
If you have a component that is being rendered server-side that requires another component which will only will be rendered in the client, it will still attempt to export that module on the server. You can either check if document exists, or require the module inline.
module.exports = typeof document !== 'undefined' ? document.createElement('div').style : null
// or
componentDidMount() {
// this is only called in the client
require('./clientComponent')
}