App.js
import React, { useRef, useEffect } from "react";
import Token from "./Token";
export default function App() {
const tokenizerRef = useRef(new Token());
useEffect(() => {
console.log("current token index: ", tokenizerRef.current.currentIndex);
}, [tokenizerRef.current.currentIndex]);
return <button onClick={tokenizerRef.current.advance}>Next</button>;
}
Token.js
class Token {
constructor() {
this.currentIndex = -1;
}
advance() {
this.currentIndex++;
}
}
export default Token;
I've a Token object ref inside App.js, and would like to watch the object field values(for this case when currentTokenIndex changes).
Currently clicking Next button, doesn't trigger the useEffect.
A better way of doing this, pointing to the right direction, will be appreciated.
We can use observer/subscriber pattern, specifically mobx for this purpose.
Token.js
import { makeAutoObservable } from "mobx";
class Token {
constructor() {
this.currentIndex = -1;
makeAutoObservable(this);
}
advance() {
this.currentIndex++;
}
}
export default Token;
App.js
import React, { useEffect } from "react";
import { observer } from "mobx-react";
const App = observer(({ tokenizer }) => {
useEffect(() => {
console.log('current index changed: ', tokenizer.currentIndex)
}, [tokenizer.currentIndex])
return (
<div>
<h1>{tokenizer.currentIndex}</h1>
<button onClick={() => tokenizer.advance()}>Next</button>
</div>
);
});
export default App;
Index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Token from "./Token";
const tokenizer = new Token();
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App tokenizer={tokenizer} />
</React.StrictMode>,
rootElement
);
Related
When I try to use createContext() the console gives me this error:
App.js:6
Uncaught TypeError: Cannot destructure property 'consoleLogFunction' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is null.
I've seen others asking questions about this here in Stack Overflow but I can't find a solution.
GlobalContext.js
import React from 'react'
import { createContext } from 'react'
export const AppContext = createContext();
function GlobalContext() {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}></AppContext.Provider>
)
}
export default GlobalContext
App.js
import "./index.css";
import { useContext, useEffect } from "react";
import { AppContext } from "./components/GlobalContext";
function App() {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return (
<AppContext>
<div>home</div>
</AppContext>
);
}
export default App;
You don't need to export 'AppContext', creating the provider and exporting that is good enough.
Try this, I've also made a couple of modifications to make it easier to use the context later:
GlobalContext.js
import React from 'react'
import { createContext, useContext } from 'react'
const AppContext = createContext();
function GlobalContext({ children }) {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}>
{ children }
</AppContext.Provider>
)
}
export default GlobalContext;
// This is a helper
export const useGlobalContext = () => useContext(AppContext);
Home.js
import { useEffect } from "react";
import { useGlobalContext } from "./components/GlobalContext";
export const Home = ({ children }) => {
const { consoleLogFunction } = useGlobalContext();
useEffect(() => {
consoleLogFunction();
}, []);
return(
<div>home</div>
)
};
App.js
import "./index.css";
import { useEffect } from "react";
import GlobalContext from "./components/GlobalContext"
import { Home } from "./components/Home";
function App() {
return(
<GlobalContext>
<Home />
</GlobalContext>
);
}
export default App;
hello man the problem is because App component is not wrapped inside GlobalContext . and in the GlobalContext component you should handle the children prop.
it will work when doing it like this example :
import { useEffect, useContext, createContext } from "react";
import "./styles.css";
export const AppContext = createContext();
function GlobalContext(props) {
const consoleLogFunction = () => {
console.log("ok");
};
return (
<AppContext.Provider value={{ consoleLogFunction }}>
{props.children}
</AppContext.Provider>
);
}
const Home = () => {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return <div>home</div>;
};
export default function App() {
return (
<GlobalContext>
<Home />
</GlobalContext>
);
}
Hope this help.
I have navigation constant which is an array of objects(web-store mega-nav). I need to use context provider and when I'm trying to use my context it's telling me NavContext' is not defined no-undef.
NavContext.js
import { createContext } from 'react'
const navigation = [...] // array of objects
const NavContext = createContext(navigation)
export default NavContext
Nav.js
import {createContext} from 'react'
import NavContext from './context/NavContext' //added
function Nav() {
return (
<NavContext.Provider> //deleted value
// childrens
</NavContext.Provider>
)
}
Sidebar.js
//then in one of the child I'm trying to call it:
import { useContext } from 'react'
import NavContext from '../context/NavContext' //added
function Sidebar(){
const nav = useContext(NavContext)
return (
{nav.map(...)} // nav is undefined
)
}
Now nav constant is undefined when I'm using useContext
You need to export the create context like this
export const NavContext = createContext(navigation)
Then import it into your child component like this
import { NavContext} from "../Nav";
//Create a new NavContext.js File.
import React, { createContext, useReducer } from "react";
export const NavContext = createContext();
const initialState = {
}
function reducer(state, action) {
return { ...state, ...action };
}
export const NavProvider = (props) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<NavContext.Provider value={{ state, dispatch }}>
{props.children}
</NavContext.Provider>
);
};
Then in your index.js file.
import { NavProvider} from "./NavContext";
ReactDOM.render(
<NavProvider>
<App />
</NavProvider>,
document.getElementById("root")
);
If that doesnt work idk what will.
Try not to pass any arguments into createContext, then pass navigation into Context provider as prop
<Context.Provider value={navigation} />
And then get the value using useContext Hook in your consumer component
For my knowledge, you have to import useContext like this.
import React, { useContext } from 'react'
Starting with GamePage, it provides 2 routes which renders the components GameList and GameDetailPage. Both work fine at first but When i refresh the page for Gamelist component, it still rerenders the page but when i refresh the page for GameDetailPage, i get the error TypeError: Cannot read property 'Location' of undefined. I do not understand why it is unable to fetch data from state whenever i refresh.
gamepage.jsx
import React from "react";
import GamesList from "../../components/games-list/game-list.component";
import { Route } from "react-router-dom";
import GameDetailPage from "../gamedetailpage/gamedetailpage.component";
import {firestore,convertCollectionsSnapshotToMap} from '../../firebase/firebase.utils'
import {connect} from 'react-redux'
import {updateFootballGames} from '../../redux/games/games.actions'
class GamePage extends React.Component {
unsubscribeFromSnapshot=null;
//whenever the component mounts the state will be updated with the football games.
componentDidMount(){
const {updateFootballGames}=this.props
const gameRef=firestore.collection('footballgames')
gameRef.onSnapshot(async snapshot=>{
const collectionsMap=convertCollectionsSnapshotToMap(snapshot)
updateFootballGames(collectionsMap)
})
}
render() {
const { match } = this.props;
return (
<div className="game-page">
<h1>games page</h1>
<Route exact path={`${match.path}`} component={GamesList} />
<Route path={`${match.path}/:linkUrl`} component={GameDetailPage}
/>
</div>
);
}
}
const mapStateToProps=state=>({
games:state.games.games
})
const mapDispatchToProps=dispatch=>({
updateFootballGames:collectionsMap=>
dispatch(updateFootballGames(collectionsMap))
})
export default connect(mapStateToProps, mapDispatchToProps)(GamePage);
gamedetailpage.component.jsx
import React from "react";
import { connect } from "react-redux";
import GamePreview from '../../components/game-preview/game-preview.component'
import GameDetails from '../../components/game-details/game-details.component'
const GameDetailPage = (props) => {
const {games, match} = props
const urlparam =match.params.linkUrl
// const games_array = Object.entries(games)
const gameObj=games[urlparam]
console.log('prop',gameObj)
return (
<div className="game-list">
<GameDetails game = {gameObj}/>
</div>
);
};
const mapStateToProps = (state) => ({
games: state.games.games,
});
export default connect(mapStateToProps)(GameDetailPage);
game_details.component.jsx
import React from 'react';
const GameDetails = (props) => {
console.log(props.game.Location)
return(
<div>
Location:{props.game.Location}
<br/>
Price:{props.game.Price}
</div>
)
}
export default GameDetails;
gamelist.component.jsx
import React from "react";
import './game-list.styles.scss'
import GamePreview from "../game-preview/game-preview.component";
import {connect} from 'react-redux'
const GameList=(props)=>{
const {games}=props
console.log(games)
const game_list=Object.entries(games)
console.log(game_list)
return (
<div className="game-list">
{game_list.map(game =>
<GamePreview game = {game[1]}/>)}
</div>
);
}
const mapStateToProps=state=>({
games:state.games.games
})
export default connect(mapStateToProps)(GameList);
gamepreview.component.jsx
import React from "react";
import "./game-preview.styles.scss";
import { withRouter, Route } from "react-router-dom";
import GamePreviewDetail from "../game-preview-detail/game-preview-detail.component";
const GamePreview = (props) => {
const { Location, Time, linkUrl, Price } = props.game;
const { history, match } = props;
return (
<div
className="game-preview"
onClick={() => history.push(`${match.url}/${linkUrl}`)}
>
<div className="game-preview-image">
<p>Picture goes here</p>
</div>
{/* <GamePreviewDetail name = {Location} price={Price}/> */}
<p>Location:{Location}</p>
<p>Price:{Price}</p>
</div>
);
};
export default withRouter(GamePreview);
app.js
import React from 'react';
import './App.css';
//import dependencies
import { Route, Switch } from "react-router-dom";
//import pages
import HomePage from './pages/homepage/homepage'
import GamesPage from './pages/gamespage/gamespage'
import SignInSignUp from './pages/signin-signup-page/signin-signup-page'
import GameDetailPage from "./pages/gamedetailpage/gamedetailpage.component";
import Header from './components/header/header.component';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
class App extends React.Component{
constructor() {
super();
this.state = {
currentUser: null
}
}
unsubscribeFromAuth = null
componentDidMount() {
this.unsubscribeFromAuth = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
// check if the snapshot has changed (subscribe)
// get the user that we just created or that already exists in the db
userRef.onSnapshot(snapshot => {
this.setState({
currentUser: {
id: snapshot.id,
...snapshot.data()}
})
})
} else {
this.setState({currentUser: userAuth})
}
})
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render(){
return(
<div>
<Header currentUser = {this.state.currentUser}/>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/games" component={GamesPage} />
<Route exact path="/signin" component={SignInSignUp} />
</Switch>
</div>
)
}
}
export default App;
I would try using useParams hook instead. Then capturing any changes of linkUrl with useEffect hook. Also introducing gameObj with useState.
useParams returns an object of key/value pairs of URL parameters. Use it to access match.params of the current <Route>.
If you're familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
Try to modify <GameDetailPage /> component as the following:
import React, { useState, useEffect } from 'react';
import { useParams } from "react-router-dom";
// other imports
const GameDetailPage = (props) => {
const { games } = props;
let { linkUrl } = useParams();
const [ gameObj, setGameObj ] = useState(null);
useEffect(() => {
if (games) {
const newGameObj = games[linkUrl];
console.log('game object', newGameObj);
setGameObj(newGameObj);
}
}, [games, linkUrl]);
return <div className="game-list">
{ gameObj && <GameDetails game={ gameObj } /> }
</div>
}
+1 - null check:
Also you can see a null check in the return statement for gameObj which helps rendering only that case once you have a value in games array with found linkUrl value.
I hope this helps!
I need to use dispatch Context API methods in _app.js.
The main limitation is that I use React hooks along with Context API, since _app.js is a Class, I can't use hooks within it.
My code:
// store.js
import React, { createContext, useContext, useReducer } from "react";
import mainReducer from "../store/reducers";
const AppStateContext = createContext();
const AppDispatchContext = createContext();
const initialState = {
filters: {
diet: {
selected: []
}
}
};
const useAppState = () => useContext(AppStateContext);
const useAppDispatch = () => useContext(AppDispatchContext);
const useApp = () => [useAppState(), useAppDispatch()];
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(mainReducer, initialState);
return (
<AppStateContext.Provider value={state}>
<AppDispatchContext.Provider value={dispatch}>
{children}
</AppDispatchContext.Provider>
</AppStateContext.Provider>
);
};
export { AppProvider, useAppState, useAppDispatch, useApp };
// _app.js
import App from "next/app";
import React from "react";
import { AppProvider } from "../store";
class MyApp extends App {
componentDidMount() {
/***********************************/
// HERE I WOULD LIKE TO USE DISPATCH
/***********************************/
}
render() {
const { Component, router, pageProps } = this.props;
return (
<AppProvider>
<Component {...pageProps} />
</AppProvider>
);
}
}
export default MyApp;
If you really want to use hooks, then just put a wrapper around _app.js like this:
import React from 'react'
import App from 'next/app'
function MyComponent({ children }) {
// You can use hooks here
return <>{children}</>
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<MyComponent>
<Component {...pageProps} />
</MyComponent>
)
}
}
export default MyApp
This is a really long post, but I really need some help :/
I will be eternally grateful if someone would be able to help.
I have managed to get Auth0 working for an application i am working on with just react. It is an Overwatch SR tracker, and is essentially just a spreadsheet so I wasn't too concerned with protecting backend routes when I make them. There isn't any private information there.
My application state/props network became too complicated to manage, and through the process of implementing redux I simply cannot get it to work. I've been at it for three days, and I'm running out of ideas. Do I need Thunk with my current Auth setup to do this? I would imagine it is async since it needs to go get something that isnt there.
Granted I am a junior Dev, and dont have much experience with authentication. Can someone take a look at my working react application and guide me in the direction of what i may need to do to set it up with redux? I do have an understanding of redux flow, so if the proper method to do this was explained to me i feel i might get it.
here is some code:
my Auth.js file :
/*eslint no-restricted-globals: 0 */
import auth0 from "auth0-js";
import jwtDecode from 'jwt-decode';
const LOGIN_SUCCESS_PAGE = '/menu';
const LOGIN_FAILURE_PAGE = '/';
export default class Auth {
auth0 = new auth0.WebAuth({
domain: "redacted.auth0.com",
clientID: "redacted",
redirectUri: "http://localhost:3000/callback",
audience: "https://redacted.auth0.com/userinfo",
responseType: "token id_token",
scope: "openid profile"
});
constructor() {
this.login = this.login.bind(this);
}
login() {
this.auth0.authorize();
}
handleAuthentication() {
this.auth0.parseHash((err, authResults) => {
if (authResults && authResuslts.accessToken && authResults.idToken) {
let expiresAt = JSON.stringify((authResults.expiresIn) * 1000 + new Date().getTime());
localStorage.setItem("access_token", authResults.accessToken);
localStorage.setItem("id_token", authResults.idToken);
localStorage.setItem("expires_at", expiresAt);
location.hash = "";
location.pathname = LOGIN_SUCCESS_PAGE;
} else if (err) {
location.pathname = LOGIN_FAILURE_PAGE;
console.log(err);
}
});
}
isAuthenticated() {
let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
return new Date().getTime() < expiresAt;
}
logout() {
localStorage.removeItem("access_token");
localStorage.removeItem("id_token");
localStorage.removeItem('expires_at');
location.pathname = LOGIN_FAILURE_PAGE;
}
getProfile() {
if (localStorage.getItem("id_token")) {
console.log(jwtDecode(localStorage.getItem("id_token")))
console.log(localStorage.getItem("id_token"));
return jwtDecode(localStorage.getItem("id_token"));
} else {
return {
name: 'Anon',
nickname: 'Anon',
picture: 'placeholder',
uid: null,
}
}
}
}
my index.js file:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import Auth from './Auth';
import { BrowserRouter } from 'react-router-dom';
const auth = new Auth();
let state = {};
window.setState = (changes) => {
state = Object.assign({}, state, changes)
ReactDOM.render(
<BrowserRouter>
<App {...state} />
</BrowserRouter>,
document.getElementById('root'));
}
/* eslint no-restricted-globals: 0*/
let getUserProfile = auth.getProfile();
let initialState = {
owSrTrackInfo: {
infoSaved: false,
accounts: [],
},
user: getUserProfile,
location: location.pathname.replace(/^\/?|\/$/g, ""),
auth,
}
window.setState(initialState);
registerServiceWorker();
my App.js file:
import React, { Component } from "react";
import "./App.css";
import Main from "./Components/Main/Main";
import Menu from "./Pages/Menu/Menu";
import NotFound from "./Components/NotFound/NotFound";
import Callback from './Components/Callback/Callback';
import Header from './Components/Header/Header';
class App extends Component {
render() {
let mainComponent = "";
switch (this.props.location) {
case "":
mainComponent = <Main {...this.props} />;
break;
case "callback":
mainComponent = <Callback />
break;
case "menu":
mainComponent = this.props.auth.isAuthenticated() ? < Menu {...this.props} /> : <NotFound />;
break;
default:
mainComponent = <NotFound />;
}
return (
<div className="app">
<Header {...this.props} />
{mainComponent}
</div>
);
}
}
export default App;
my Callback.js component:
import React, {Component} from 'react';
import Auth from '../../Auth'
export default class Callback extends Component {
componentDidMount() {
const auth = new Auth();
auth.handleAuthentication();
}
render() {
return(
<p className="loading">Loading.....</p>
)
}
}
My current MAIN.js component:
import React, { Component } from "react";
export default class Main extends Component {
render() {
console.log(this.props.auth.getProfile())
return (
<div className="container">
<div className='container--logged-out'>
<h1 className="heading u-margin-bottom-small">welcome to redacteds' overwatch sr tracker</h1>
<p>Hello there {this.props.user.nickname}! Sign in single click or email via Auth0 so we can save your results, and make the app usable by more than one person. I intend for more than one person to use this, so just to launch it and so the app knows your spreadsheet from someone elses I'll tie each user to their own UID. Feel free to come back, log in, and get your spreadsheet for the season back anytime.</p>
</div>
Go to the app menu!
<button onClick={() =>this.props.auth.getProfile()}>asdgkljsdngk</button>
</div>
);
}
}
my current HEADER.js component:
import React, { Component } from 'react';
export default class Header extends Component {
render() {
return (
<header className="header">
<h1 className='header__text'>SR TRACKER</h1>
{this.props.auth.isAuthenticated() ?
<button className='btn btn--logout' onClick={() => this.props.auth.logout()}>Logout</button>
:
<button className='btn btn--login' onClick={() => this.props.auth.login()}>Login or Sign Up</button>}
</header>
)
}
}
I simply want to map this authentication to a redux store instead to be consitent with the rest of my app (when redux is implemented) I have blown it away and started over multiple times, but a rough idea of what my redux flow might look like is like this template i use and have successfully implemented several times:
redux store:
import { createStore, compose, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import rootReducer from './reducers/rootReducer';
export default function configureStore(initialState) {
const middleware = [
createLogger({
collapsed: false,
duration: true,
diff: true,
}),
thunk,
];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.devToolsExtension ? window.devToolsExtension() : format => format, // add support for Redux dev tools),
),
);
return store;
}
actionTypes.js in actions folder:
const actions = {
GET_FRIENDS: 'GET_FRIENDS',
REMOVE_FRIEND: 'REMOVE_FRIEND',
GET_MOVIES: 'GET_MOVIES',
GET_MOVIES_SUCCESS: 'GET_MOVIES_SUCCESS',
GET_MOVIES_FAILURE: 'GET_MOVIES_FAILURE',
DEVIN_FUN: 'DEVIN_FUN',
};
export default actions;
Sample actions page:
import axios from 'axios';
import actionTypes from './actionTypes';
export const getMoviesSuccess = data => {
return {
type: actionTypes.GET_MOVIES_SUCCESS,
data,
};
};
export const getMoviesFailure = () => {
return {
type: actionTypes.GET_MOVIES_FAILURE,
};
};
export const devinIsHavingFun = () => {
return {
type: actionTypes.DEVIN_FUN,
};
};
export const retrieveMovies = () => {
return function(dispatch) {
const API_KEY = 'trilogy';
dispatch(devinIsHavingFun());
axios
.get(`http://www.omdbapi.com?apikey=${API_KEY}&s=frozen`)
.then(data => {
dispatch(getMoviesSuccess(data.data.Search));
})
.catch(error => {
console.log(error);
dispatch(getMoviesFailure());
});
};
};
in the reducers folder wed have some files like initialState.js and root reducer that look like this respectively:
initialState.js:
export default {
friends: [],
movies: [],
};
rootReducer.js:
import { combineReducers } from 'redux';
import friends from './friendReducer';
import movies from './movieReducer';
const rootReducer = combineReducers({
friends,
movies,
});
export default rootReducer;
and a sample reducer:
import actionTypes from '../actions/actionTypes';
import initialState from './initialState';
export default function movieReducer(state = initialState.movies, action) {
switch (action.type) {
case actionTypes.GET_MOVIES_SUCCESS: {
return action.data;
}
default: {
return state;
}
}
}
I just dont know what to do. Do i need to use thunk? am I overthinking this? I'm pulling my hair out.
I also do connect my components in this fashion when redux is implemented :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as friendActionCreators from './actions/friendActions';
import * as movieActionCreators from './actions/movieActions';
....................
function mapStateToProps(state) {
return {
myFriends: state.friends,
movies: state.movies,
};
}
function mapDispatchToProps(dispatch) {
return {
friendActions: bindActionCreators(friendActionCreators, dispatch),
movieActions: bindActionCreators(movieActionCreators, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Please let me know if anyone can point me in the right direction. thank you so much in advance.