Dispatching Socket.io API response to React Components using Redux - javascript

I'm receiving the data, but the issue is I'm only able to access it inside of the same file api.js. As I can tell, it's not returning any value. I did a lot of variations of this code snippets. It's either returning the initial state or undefined. The interesting thing is, if there is an error, it would fill out the error object. Sorry for so much code, but can someone please help me to successfully implement socket.io with React/ Redux.
This is what I got so far:
api.js
Here I'm successfully connecting to the API, receiving the data, but I'm not able to use it outside of this file. If a return an object it will be undefined, but if I console.log it or even return a console.log and use the file in other component/ file, the data will show up in my console, but only that way, and only in my console ... can't dispatch the data, and re-use it all over the app that I'm trying to build.
import axios from 'axios';
import io from "socket.io-client";
export function fetchData() {
const configUrl = 'API endpoint';
axios.get(configUrl).then(res => {
const socketUrl = res.data.config.liveDistributionSSL;
const socket = io(socketUrl);
socket.on('connect', function () {
socket.emit('subscribe', {
subscribeMode: 'topSportBets',
language: {
default: 'en'
},
deliveryPlatform: 'WebSubscribe',
playerUuid: null,
subscribeOptions: {
autoSubscribe: true,
betCount: 3,
excludeMeta: false,
resubscriptions: 0,
fullBetMeta: true,
browser: {}
}
});
let relevantData = {}; // Object that I'm trying to assign values to and return
socket.on('message', async (message) => {
switch (message.type) {
case 'state': // We have all the data needed to show
relevantData = await Object.values(Object.values(message.data)[9]);
break;
case 'currentMatches':
// We have matches to update
console.log('Matches =>', message.contentEncoding);
break;
case 'betchange':
// We have match bets to update
console.log('Match bets =>', message.contentEncoding);
break;
default: break;
}
return relevantData;
});
socket.on("disconnect", () => console.log("Client disconnected"));
});
});
}
actions/index.js
This is my main action creator. The code you're seeing was my last attempt to try out a new approach, still the same result.
import { GET_DATA, GET_ERROR } from './types';
import { fetchData } from '../api'
export const getDataAsync = () => async dispatch => {
try {
const response = await fetchData();
dispatch({ type: GET_DATA, payload: response });
} catch (e) {
dispatch({ type: GET_ERROR, payload: 'Something went wrong ', e });
}
};
reducers/data_reducer.js
Here I'm making a simple reducer and depending on the payload change the initial state
import { GET_DATA, GET_ERROR } from '../actions/types';
const INITIAL_STATE = {
apiData: {},
errorMessage: {}
};
const dataReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_DATA:
return { ...state, apiData: action.payload };
case GET_ERROR:
return { ...state, errorMessage: action.payload };
default:
return state;
}
}
export default dataReducer;
reducers/root_reducer.js
Here I'm combining the reducers and later on implement my root_reducer to my store configuration
import { combineReducers } from 'redux';
import dataReducer from './data_reducer';
const rootReducer = combineReducers({
allData: dataReducer
});
export default rootReducer;
PrimaryLayoutContainer.js
*This would be my main layout container where I'm implementing routing, display the PrimaryLayout component, and where **I'm trying to pass down the values ass props*
import React, { Component } from 'react';
import {connect} from 'react-redux';
import PrimaryLayout from "../components/PrimaryLayout";
import { withRouter } from 'react-router'
import * as myData from '../actions';
class PrimaryLayoutContainerComponent extends Component {
render() {
return (
<div>
<PrimaryLayout history={this.props.history}
allData={this.props.allData}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
allData: state.allData
}
}
export default withRouter(connect(mapStateToProps, myData)(PrimaryLayoutContainerComponent));
PrimaryLayout.js
Where I'm trying to implement the data, all of the routing, and display main components, etc, but here I stopped because I'm not getting the needed data
import React, {Component} from 'react';
import {Switch, Route, Redirect, Link} from 'react-router-dom';
import Favorites from './Favorites';
... a lot of other imports of my components
class PrimaryLayout extends Component {
constructor(props) {
super(props);
this.state = {
currentRoute: '',
// data: {}
}
}
componentWillReceiveProps(nextProps) {
this.setState({
currentRoute: nextProps.history.location.pathname
})
}
componentWillMount() {
this.setState({
currentRoute: this.props.history.location.pathname
})
}
componentDidMount() {
console.log(this.props); // Where i'm trying to get access to the data
}
render() {
const {currentRoute} = this.state;
return (
<div className='main-nav'>
<nav className="topnav">
<ul>
<li className={currentRoute === "/favorites" ? "active" : ""}>
<Link to="/favorites"><div className='star'></div> Favorites </Link> </li>
<li className={currentRoute === "/football" ? "active" : ""}>
<Link to="/football"><div className='football'></div> Football </Link> </li>
<li className={currentRoute === "/basketball" ? "active" : ""}>
<Link to="/basketball"><div className='basketball'></div> Basketball </Link> </li>
<li className={currentRoute === "/tennis" ? "active" : ""}>
<Link to="/tennis"><div className='tennis'></div> Tennis </Link> </li>
<li className={currentRoute === "/baseball" ? "active" : ""}>
<Link to="/baseball"><div className='baseball'></div> Baseball </Link> </li>
<li className={currentRoute === "/waterpolo" ? "active" : ""}>
<Link to="/waterpolo"><div className='waterpolo'></div> Waterpolo </Link> </li>
</ul>
</nav>
<main>
<Switch>
<Route path='/favorites' component={Favorites} />
<Route path='/football' component={Football} />
<Route path='/basketball' component={Basketball} />
<Route path='/tennis' component={Tennis} />
<Route path='/baseball' component={Baseball} />
<Route path='/waterpolo' component={Waterpolo} />
<Route path='/volleyball' component={Volleyball} />
<Route path='/handball' component={Handball} />
<Route path='/formula1' component={Formula} />
<Redirect to="/football"/>
</Switch>
</main>
</div>
)
}
}
export default PrimaryLayout;
index.js
This would be my main index.js file where I'm configuring the store and rendering elements to the DOM
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
import './assets/styles/App.css';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers/root_reducer';
import thunk from 'redux-thunk';
function configureStore() {
return createStore(
rootReducer,
applyMiddleware(thunk)
);
}
const myStore = configureStore();
ReactDOM.render(
<Provider store={myStore}>
<App />
</Provider>,
document.getElementById('root')
)
NOTE:
Like I said before, I'm successfully connecting to the API endpoint, receiving the necessary data, but I can only manipulate it inside of the same file api.js. I Google all day long, but I didn't find anything relevant to my issue. There's a lot of examples with PHP, Java, but not so much with React, Redux, etc ... I never used Socket.io before, so please my dear friends, help me ... :/
The main question here is:
How can I successfully implement Scoket.io and use Redux to dispatch the API data throw out all of my main React components, instead of doing it very DRY, meaning implement the same logic in every component, and every time to get all of the data, instead of only the relevant one.
If I can do it in one place (outside of the api.js file) I can do it everywhere, and that answer is more than appreciated and will be accepted immediately.

I changed the api.js file. the async - await was causing problems, plus the return statement was in the wrong place. A rookie mistake.
import axios from 'axios';
import io from "socket.io-client";
export const fetchData = () => {
let apiData = {}; // Object that I'm trying to assign values to and return
apiData.bets = [];
apiData.matches = [];
const configUrl = 'API URI';
axios.get(configUrl).then(res => {
const socketUrl = res.data.config.liveDistributionSSL;
const socket = io(socketUrl);
socket.on('connect', function () {
socket.emit('subscribe', {
subscribeMode: 'topSportBets',
language: {
default: 'en'
},
deliveryPlatform: 'WebSubscribe',
playerUuid: null,
subscribeOptions: {
autoSubscribe: true,
betCount: 3,
excludeMeta: false,
resubscriptions: 0,
fullBetMeta: true,
browser: {}
}
});
socket.on('message', (message) => {
switch (message.type) {
case 'state': // We have all the data needed to show
apiData.bets = Object.assign(message.data.bets);
apiData.matches = Object.assign(message.data.matches);
break;
// ... etc ...
default: break;
}
});
socket.on("disconnect", () => console.log("Client disconnected"));
});
});
return apiData;
}
After that, I implemented a better store configuration. I actually made an external file for it, three to be exact, depending on the development progress (dev, stage, prod). Here is the configureStore.dev.js file:
import thunk from 'redux-thunk';
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import rootReducer from '../reducers/root_reducer';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
export default function configureStore(initialState) {
return createStore(
rootReducer,
compose(
applyMiddleware(thunk, reduxImmutableStateInvariant()),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
}
And finally what I did to make it all work, I changed the PrimaryLayoutContainerComponent.js to look like this. And with all that simple, but important changes I was able to access all the relevant data as its being updated in real-time throughout the entire app.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PrimaryLayout from "../components/PrimaryLayout";
import { withRouter } from 'react-router'
import * as myData from '../actions';
// Since this is our Primary Layout Container here we in addition to mapping state to props also
// are dispatching our props and binding our action creators, also using 'withRouter' HOC
// to get access to the history object’s properties and make it easy to navigate through our app.
class PrimaryLayoutContainerComponent extends Component {
render() {
return (
<div>
<PrimaryLayout history={this.props.history}
allData={this.props.allData}
getDataAsync={this.props.actions.getDataAsync}
/>
</div>
)
}
}
const mapStateToProps = (state) => {
return {allData: state.allData.apiData}
}
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators({...myData}, dispatch)
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(PrimaryLayoutContainerComponent));
A little sidenote: I always answer my own questions instead of just deleting them with n I find the resolution myself. I do that for the simple fact that it might help someone in the future.

Related

Unable to access REDUX State in multple Pages and Components

I need some help figuring this issue out. After setting up Redux store and reducer in my app, I was able to successfully log and render updated state upon click in one, but not multiple pages. Below are steps and code sample:
Step1:
I installed Redux and wrapped the store around my entire app
// _app.js
import Layout from '../components/Layout';
import { SessionProvider } from 'next-auth/react';
import store from '../store';
import { Provider } from 'react-redux';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<>
<Provider store={store}>
<SessionProvider session={session}>
<Layout>
<Component {...pageProps} />
<ToastContainer />
</Layout>
</SessionProvider>
</Provider>
</>
);
}
export default MyApp;
Step 2:
Setup an instance of a slice, Store and reducer
// mySlice.js
import { createSlice} from '#reduxjs/toolkit';
const initialState = {
user: {
role: ""
},
};
export const userStatusSlice = createSlice({
name: 'userStatus',
initialState,
reducers: {
userInfo: (state, action) => {
state.user.role = action.payload.role; // only this value comes from payload onClick
},
},
});
// Action creators are generated for each case reducer function
export const { userInfo } = userStatusSlice.actions;
export default userStatusSlice.reducer;
Step 3: Store...
//store.js
import { configureStore } from '#reduxjs/toolkit';
import userStatusSlice from './slices/userSlice/userStatus';
export default configureStore({
reducer: {
userStatus: userStatusSlice,
},
});
Step 4: Setup pages and React Hook useSelector, and tried accessing dispatched actions set as state variables in multiple pages. On one page I was able to fetch the data successfully, but not on the other page(s)
//First Page
import { useSession, getSession } from 'next-auth/react';
import { useSelector } from 'react-redux';
const firstPage = () => {
const { data: session } = useSession();
const { role } = useSelector((state) => state.userStatus.user);
console.log(role); // There is role successfully logged to the console
return (
<>
</>
);
};
export default firstPage;
//Second page.js
import { useSession } from 'next-auth/react';
import { useSelector } from 'react-redux';
const secondPage = () => {
const { data: session } = useSession();
const { role } = useSelector((state) => state.userStatus.user);
console.log(role) // There is NO role - why?
return (
<>
</>
);
};
export default secondPage;
I appreciate all input to help resolving this issue. Thanks in advance

How to dispatch an action from inside getInitialProps?

I am trying to implement Redux in a Next.js app and have problems getting the dispatch function to work in getInitialProps. The store is returned as undefined for some reason that I cannot figure out. I am using next-redux-wrapper. I have followed the documentation on next-redux-wrapper GitHub page but somewhere on the way it goes wrong. I know the code is working - I used axios to directly fetch the artPieces and then it worked just fine but I want to use Redux instead. I am changing an react/express.js app to a Next.js app where I will use the API for the basic server operations needed. This is just a small blog app.
Here is my store.js:
import { createStore } from 'redux';
import { createWrapper, HYDRATE } from 'next-redux-wrapper';
// create your reducer
const reducer = (state = { tick: 'init' }, action) => {
switch (action.type) {
case HYDRATE:
return { ...state, ...action.payload };
case 'TICK':
return { ...state, tick: action.payload };
default:
return state;
}
};
// create a makeStore function
const makeStore = (context) => createStore(reducer);
// export an assembled wrapper
export const wrapper = createWrapper(makeStore, { debug: true });
And here is the _app.js:
import './styles/globals.css';
import { wrapper } from '../store';
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
export default wrapper.withRedux(MyApp);
And finally here is where it does not work. Trying to call dispatch on the context to a sub component to _app.js:
import React from 'react';
import { ArtPiecesContainer } from './../components/ArtPiecesContainer';
import { useDispatch } from 'react-redux';
import axios from 'axios';
import { getArtPieces } from '../reducers';
const Art = ({ data, error }) => {
return (
<>
<ArtPiecesContainer artPieces={data} />
</>
);
};
export default Art;
Art.getInitialProps = async ({ ctx }) => {
await ctx.dispatch(getArtPieces());
console.log('DATA FROM GETARTPIECES', data);
return { data: ctx.getState() };
};
This should probably work with "next-redux-wrapper": "^7.0.5"
_app.js
import { wrapper } from '../store'
import React from 'react';
import App from 'next/app';
class MyApp extends App {
static getInitialProps = wrapper.getInitialAppProps(store => async ({Component, ctx}) => {
return {
pageProps: {
// Call page-level getInitialProps
// DON'T FORGET TO PROVIDE STORE TO PAGE
...(Component.getInitialProps ? await Component.getInitialProps({...ctx, store}) : {}),
// Some custom thing for all pages
pathname: ctx.pathname,
},
};
});
render() {
const {Component, pageProps} = this.props;
return (
<Component {...pageProps} />
);
}
}
export default wrapper.withRedux(MyApp);
and Index.js
import { useEffect } from 'react'
import { useDispatch } from 'react-redux'
import { END } from 'redux-saga'
import { wrapper } from '../store'
import { loadData, startClock, tickClock } from '../actions'
import Page from '../components/page'
const Index = () => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(startClock())
}, [dispatch])
return <Page title="Index Page" linkTo="/other" NavigateTo="Other Page" />
}
Index.getInitialProps = wrapper.getInitialPageProps(store => async (props) => {
store.dispatch(tickClock(false))
if (!store.getState().placeholderData) {
store.dispatch(loadData())
store.dispatch(END)
}
await store.sagaTask.toPromise()
});
export default Index
For the rest of the code you can refer to nextjs/examples/with-redux-saga, but now that I'm posting this answer they're using the older version on next-redux-wrapper ( version 6 ).

React Redux Life Cycle Confussion

I have been reading several documents and watching videos regarding React Redux, but since all of them are different I wasn't able to apply that knowledge to some real project.
I will try to enumarate the process in order to use React Redux together.
Directory Structuring
project
src
components
User
index.js (Container component)
page.js (Presentational component)
actions
users.js
index.js (exports actionCreators combination)
reducers
users.js
index.js (exports reducer combination with combineReducers
constants
actionTypes.js
services
users.js
index.js
store.js
public
index.html
Redux Setup
We create constants in project/src/constants/actionTypes.js:
export const CREATE_USER = 'CREATE_USER';
export const DELETE_USER = 'DELETE_USER';
export const UPDATE_USER = 'UPDATE_USER';
We create actionCreators en project/src/actions/users.js y luego se combinan en project/src/actions/index.js:
users.js
import { CREATE_USER } from '../constants/actionTypes';
export default function createUser(user) {
type: CREATE_USER,
user
}
index.js
import { createUser } from './users';
export default {
createUser
}
We create reducers in project/src/reducers/users.js and they are combined in project/src/reducers/index.js using combineReducers():
users.js
import { CREATE_USER, UPDATE_USER, DELETE_USER } from '../constants/actionTypes';
import { createUser } from '../services/users';
const initialState = {
name: '',
password: '',
email: ''
}
export default function users(state = initialState, action) {
switch (action.type) {
case CREATE_USER:
state = createUser(action.user);
return state;
}
}
index.js
import users from './users';
export default combineReducers({
users
})
We create store in project/src/store.js:
import { createStore } from 'redux';
import reducers from './reducers';
export const store = createStore(reducers);
React Redux Setup
We wrap component application <Provider> in project/src/index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './store';
const Root = () => (
`
<Provider store={store}>
<App />
</Provider>
`
)
ReactDOM.render(Root, document.getElementById('root');
We transform component state to properties with mapStateToProps in project/src/components/User/index.js:
import React, { Component } from 'react';
import { createUser } from '../../actions/users';
import Page from './page';
class User extends Component {
render() {
return <Page users={this.props.users} />
}
}
const mapStateToProps = state => ({
users: this.props.users
// what is mapped here?
});
const mapDispatchToProops = dispatch => ({
// what about here?
});
export default connect(mapStateToProps, mapDispatchToProps)(User);
So, the question would be, is this React-Redux cycle well formed? What is missing or wrong?
Yes, the folder structure works well. As for the "fetch" or "service" functionality you're talking about, I'll give you an example of what actions and reducers both should, in a basic example, do.
So if you're working with a backend which you're "fetching" anything from, I'd recommend adding that functionality in the action, not the reducer:
import { USERS_FETCHED } from '../constants/actionTypes';
import { baseUrl } from "../constants/baseUrl";
const usersFetched = users => ( { // action to dispatch
type: USERS_FETCHED,
users,
} );
export const fetchUsers = () => ( dispatch ) => { // export for mapDispatchToProps
request( `${ baseUrl }/users` )
.then( response => {
dispatch( usersFetched( response.body ) ); // dispatch the action to reducer
} )
.catch( console.error );
}; // in your case you import createUser(), but it works either way
Now the action is concerned with functionality, in contrast the reducer is only concerned with managing the Redux state:
import { USERS_FETCHED } from "../constants/actionTypes";
export default ( state = null, action = {} ) => {
switch ( action.type ) {
case USERS_FETCHED:
return action.users;
default:
return state;
}
};
Functionality in the reducer is fine, but it should only be concerned with managing state. You can imagine how cluttered the code could get if you start fetching any data here, not to mention problems with asynchronicity. Of course, this is just one way to do it, but it works solidly. Hope this helps you in some way.

React-Auth0? Working! React-Redux-Auth0? I am losing my mind :/

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.

React/Redux - Why is my action/reducer not working?

Newbie here. Trying to get my Redux action to talk to my reducer and update the store. - TL;DR question further down:
Context: I am only really getting issues now that i'm trying to split out my actions/reducers into separate files and folders. Prior to this i got everything working, when it was all in one file, sort of thing.
So... I have 3 files.
i) A Client.js (top level, where my store is), which takes my reducer in and then uses provider to get the store to <Main />
Main.js then uses mapDispatchToProps to get a toggleLogin action(creator) into its onClick prop.
This is then passed down to <LoginButton /> where onClick can be clicked. And when it is, i can get a console log from the toggleLogin action-creator. But any attempt to return { type: 'TOGGLE_LOGIN' } sees either a:
'ReferenceError: store is not defined' or no change... and i have to console.log either side to find the problem sits within that return {} area. So...
TL;DR Question why wont my reducer pick-up that action and update the store? Its driving me nutty.
ClientJS:
import React from 'react';
import ReactDOM from 'react-dom';
import Redux from 'redux';
import Main from './Components/Main'
import { myReducer } from './Reducers/reducer';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
const store = createStore(myReducer);
ReactDOM.render(
<Provider store={store}>
<Main />
</Provider>,
document.getElementById('root')
);
Main.Js
import React from 'react';
import LoginButton from './LoginButton';
import PlayButton from './PlayButton';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { toggleLogin } from '../actions/toggleLogin';
const Main = (props) => {
const { onClick} = props;
return (
<div>
<h1>hello world</h1>
<LoginButton onClick={onClick} />
<PlayButton />
</div>
);
};
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators({
onClick: toggleLogin,
}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Main);
reducer.js:
export const myReducer = (state = { isLoggedIn: false }, action) => {
switch (action.type) {
case 'TOGGLE_LOGIN':
return {
isLoggedIn: !action.isLoggedIn
}
default:
return state;
}
};
LoginButton.js
import React from 'react';
import Redux from 'redux';
const LoginButton = (props) => {
const { onClick } = props;
return (
<div>
<button onClick={onClick}>LOGIN/LOGOUT</button>
</div>
);
};
export default LoginButton;
toggleLogin.js (action creator):
export function toggleLogin() {
console.log('this works');
store.dispatch({
type: 'TOGGLE_LOGIN',
});
console.log('this doesnt work');
}
toggleLogin should just return {type: 'TOGGLE_LOGIN'}. bindActionCreators is what wraps that function in a store.dispatch call, so you are basically trying to dispatch what another dispatch returns.
Also you get that 'ReferenceError: store is not defined' exception because inside your function there is no store reference.
Just closing the loop on this one - looks like it was a mesh of things:
i) my TOGGLE_LOGIN was indeed trying to dispatch. It should've just been returning an object.
ii) i did need bindActionCreators but it would've been more appropriate with more than one action.
iii) most importantly - when i was changing my TOGGLE_LOGIN to return an object... i was putting two console logs either side of it, for debugging purposes. But being a newbie/absent minded, i totally overlooked that this was effectively putting a function after a return statement. Once i took the console-logs off, i was up and running. Thanks everyone.

Categories