Not to able to get `props` in react js - next js app - javascript

I'm working on Server Side Rendering react app with React Js and Next Js as my framework, and I'm trying to fetch initial props with getServerSideProps method by referring to the document (next js doc for getServerSideProps) too.
But I'm always getting empty props object as {} on every request. For now, I'm just trying to pass a dummy text in props.
How can I get my props on the initial load?
Please refer my code below
import React from "react";
import { Provider } from "react-redux";
import ConfigureStore from "../Store";
const Home = props => {
const store = ConfigureStore();
console.log("props", props);
return (
<Provider store={store}>
<div>My content</div>
</Provider>
);
};
// This gets called on every request
export async function getServerSideProps() {
const data = "Hello World";
return { props: data };
}
export default Home;

The problem with getServerSideProps is that it can only be exported from a page. You can’t export it from non-page files. Same goes with getStaticProps.
So you can not use it in partial components.
Which means you can only use it directly on files in /page directory.

import React from "react";
import { Provider } from "react-redux";
import ConfigureStore from "../Store";
const Home =({ data }) => {
console.log("data", data);
return (
<div>My content</div>
);
};
// This gets called on every request
export async function getServerSideProps() {
const data = "Hello World";
return { props: {data :"Hello World"} };
}
export default Home;

Related

Trying to read an array from a file in React, but it comes up undefined

I'm new to React and am working on a small project. I'm trying to figure out why React won't read the data from my dataArray.js file. It comes up undefined when I console.log it. I made sure the data was being exported, the data was connected to the App.js file, and I have the data listed in the state.
I'm stumped as to what else to try.
import "./styles.css";
import { receiptsData } from "./dataArray";
import { Component } from "react";
import Receipt from "./components/Receipt";
class App extends Component {
state = {
receiptsData
};
render() {
console.log(receiptsData);
return (
<div className="App">
<Receipt receipts={this.state.receiptsData} />
</div>
);
}
}
export default App;
I have a copy on condesandbox.io: https://codesandbox.io/s/korillaapp-0pm5y3
I know I'm getting other errors as well, but I think it's tied to not being able to read the data from the dataArray.js file.
You have a default export in dataArray.js and named import in App.js.
So or do export const receiptsData = ... in dataArray.js, or import it as import receiptsData from "./dataArray"; in App.js
You exported your array as default export, so it should be imported like this :
import receiptsData from "./dataArray";
Or change your export like this :
export const receipts = [...]
1) Replace Receipt.js with below code
import ReceiptItem from "./ReceiptItem";
const Receipt = (props) => {
const { receipts } = props;
const receiptList = receipts.map((receipt, idx) => {
return(<div>
<ReceiptItem receipt={receipt} key={idx} />
</div>);
});
return (
<div>{receiptList}</div>
)
};
export default Receipt;
2) Add below line of code in the last in ReceiptItem.js
export default ReceiptItem;
3) You have default export receiptsData from dataArray.js so use receiptsData with {} in App.js

How to fetch from getStaticProps and display on other pages? [duplicate]

I'm using Next.js with context API and styled components and I can't seem to get getStaticProps working.
I have read other posts and often they talk about the custom _app which I do have but I never ran into the issue before using context API. I have also tried the getInitialProps function and to no avail.
I should also note that even after not including the context wrapper I don't get a response from a function so I'm not at all sure of where to look.
Here is my code. Can you see what's going on?
import React from 'react';
import fetch from 'node-fetch';
export default function Header(props) {
console.log(props.hi);
return <div>Hey dis header</div>;
}
export async function getStaticProps(context) {
return {
props: {
hi: 'hello',
},
};
}
I have tried logging from the function but nothing is logging so I would imagine the problem is that the function isn't running for whatever reason.
Heres my custom _app file
import { GlobalContextWrapper } from 'context';
import Header from 'components/header/Header';
import App from 'next/app';
function MyApp({ Component, pageProps }) {
return (
<GlobalContextWrapper>
<Header />
<Component {...pageProps} />
<p>footer</p>
</GlobalContextWrapper>
);
}
MyApp.getInitialProps = async (appContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps };
};
export default MyApp;
Here is my context file
import { useReducer } from 'react';
import initialState from './intialState';
import reducer from './reducer';
import GlobalStyle from '../GlobalStyle';
import theme from '../theme';
import { ThemeProvider } from 'styled-components';
export const GlobalContext = React.createContext();
export function GlobalContextWrapper({ children }) {
const [globalState, dispatch] = useReducer(reducer, initialState);
return (
<GlobalContext.Provider value={{ globalState, dispatch }}>
<GlobalStyle />
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</GlobalContext.Provider>
);
}
The issue was that i was not exporting this function from a page but instead a component and a custom app file.
Does anyone know a way i can get around this? The problem is that i have a header that gets data from a response and i want this header to be shown on every page without having to manually add it to each page along with repeating the getStaticProps function
A solution based on your code is just getting data in your _app.js - getInitialProps and pass to the Header
function MyApp({ Component, pageProps }) {
return (
<GlobalContextWrapper>
<Header data={pageProps.header}/>
<Component {...pageProps} />
<p>footer</p>
</GlobalContextWrapper>
);
}
MyApp.getInitialProps = async (appContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
const headerData = ....
return { ...appProps, header: headerData };
};

React useEffect Hook not Triggering on First Render with [] Dependencies

I'm getting data via an Axios GET request from a local API and trying to save the data in a Context Object.
The GET request works properly when I run it outside the Context Provider function. But when I put it within a UseEffect function with no dependencies - ie. useEffect( () => /* do something*/, [] )the useEffect hook never fires.
Code here:
import React, { createContext, useReducer, useEffect } from 'react';
import rootReducer from "./reducers";
import axios from 'axios';
import { GET_ITEMS } from "./reducers/actions/types";
export const ItemsContext = createContext();
function ItemsContextProvider(props) {
const [items, dispatch] = useReducer(rootReducer, []);
console.log('this logs');
useEffect(() => {
console.log('this does not');
axios.get('http://localhost:27015/api/items')
.then(data => dispatch({type: GET_ITEMS, payload: data}))
}, [])
return (
<ItemsContext.Provider value={{items, dispatch}}>
{ props.children }
</ItemsContext.Provider>
);
}
export default ItemsContextProvider;
I never see 'this does not' in the console (double and triple checked). I'm trying to initialise the context to an empty value at first, make the GET request on first render, and then update the context value.
I'd really appreciate any help on what I'm doing wrong.
EDIT - Where Context Provider is being rendered
import React from 'react';
import AppNavbar from "./Components/AppNavbar";
import ShoppingList from "./Components/ShoppingList";
import ItemModal from "./Components/ItemModal";
//IMPORTED HERE (I've checked the import directory is correct)
import ItemsContextProvider from "./ItemsContext";
import { Container } from "reactstrap"
import "bootstrap/dist/css/bootstrap.min.css";
import './App.css';
function App() {
return (
<div className="App">
<ItemsContextProvider> //RENDERED HERE
<AppNavbar />
<Container>
<ItemModal />
<ShoppingList /> //CONSUMED HERE
</Container>
</ItemsContextProvider>
</div>
);
}
export default App;
I have it being consumed in another file that has the following snippet:
const {items, dispatch} = useContext(ItemsContext);
console.log(items, dispatch);
I see console logs showing the empty array I initialised outside the useEffect function in the Context Provider and also a reference to the dispatch function.
I had the same problem for quite a while and stumbled upon this thred which did not offer a solution. In my case the data coming from my context did not update after logging in.
I solved it by triggering a rerender after route change by passing in the url as a dependency of the effect. Note that this will always trigger your effect when moving to another page which might or might not be appropriate for your usecase.
In next.js we get access to the pathname by using useRouter. Depending on the framework you use you can adjust your solution. It would look something like this:
import React, { createContext, useReducer, useEffect } from 'react';
import rootReducer from "./reducers";
import axios from 'axios';
import { GET_ITEMS } from "./reducers/actions/types";
import { useRouter } from "next/router"; // Import the router
export const ItemsContext = createContext();
function ItemsContextProvider(props) {
const [items, dispatch] = useReducer(rootReducer, []);
const router = useRouter(); // using the router
console.log('this logs');
useEffect(() => {
console.log('this does not');
axios.get('http://localhost:27015/api/items')
.then(data => dispatch({type: GET_ITEMS, payload: data}))
}, [router.pathname]) // trigger useEffect on page change
return (
<ItemsContext.Provider value={{items, dispatch}}>
{ props.children }
</ItemsContext.Provider>
);
}
export default ItemsContextProvider;
I hope this helps anyone in the future!
<ItemsContextProvider /> is not being rendered.
Make sure is being consumed and rendered by another jsx parent element.

How to use dynamic imports for redux 'actions' for bundle size optimisation

I am trying to optimise the initial page bundle size for an application. I am trying to defer loading the firebase bundle until I load a component that uses redux to make database calls.
Following is the actions file:
import { DB } from '../../firebase/initialize';
export const addText = (text, callback) => async dispatch => {
dispatch({
type: 'ADD_TEXT',
status: 'started',
});
DB.collection('texts').then(() => {
// Do something
});
};
This is loading firebase which is loading approx 100KB of code. I wanted to do load this code only after the site has completed loading.
So, I am lazy loading the component TextList that has dependency to redux action which uses firebase to get data. I was expecting this would make my actions and firebase be part of a different bundle created for TextList component and its dependency. But this is not the case.
// import react and others
import configureStore from './redux/stores/store';
import Home from './components/molecules/home/home';
ReactDOM.render(
<Provider store={configureStore()}>
<Suspense fallback={<div>Loading...</div>}>
<Home />
</Suspense>
</Provider>,
document.getElementById('root')
);
import React, { Component, lazy } from 'react';
const TextList = lazy(() => import('../../compounds/TextList/text-list'));
class Home extends Component {
render() {
return (
<div className="home-page">
<TextList />
</div>
);
}
}
export default Home;
And when Home loads, it loads redux actions at last:
import React, { Component, Suspense, lazy } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../redux/actions/actions';
class TextList extends Component {
componentDidMount() {
this.props.fetchSnippet();
}
render() {
return // template
}
}
const mapStateToProps = state => ({
...state,
});
export default connect(
mapStateToProps,
actions
)(TextList);
What approach should I follow to lazy load firebase and component using the same.
You can use a dynamic import for the firebase module in your actions file :shrug:
const getDB = async () => await import('../../firebase/initialize');
export const addText = (text, callback) => async dispatch => {
dispatch({
type: 'ADD_TEXT',
status: 'started',
});
const DB = await getDB();
DB.collection('texts').then(() => {
// Do something
});
};

How would I dispatch an redux action from my API module?

I'm building an app in React Native, using the Redux methodology.
I want to be able to dispatch actions from my API "module".
Potentially, every API request could time out (or fail), and if that happens I want to dispatch an action to my global reducer (which handles the errorBar message and state). I'd rather not dispatch that message for every result (every API request) inside the scenes or components.
My structure is as follows (most content stripped):
index.android.js
import React from 'react';
import { AppRegistry } from 'react-native';
import configureStore from './app/store/configureStore'; // combines all reducers
const store = configureStore();
import RootContainer from './app/containers/rootContainer';
import { Provider } from 'react-redux';
const App = () => (
<Provider store={store}>
<RootContainer/>
</Provider>
);
// Register our app
AppRegistry.registerComponent('ReduxTest', () => App);
rootContainer:
import { connect } from 'react-redux';
import RootScene from '../components/scenes/RootScene';
import { hideSplash, showSplash, setSplashMessage } from '../actions/splashActions';
function mapStateToProps(state) {
return {
global: state.globalReducer,
splash: state.splashReducer
};
}
export default connect(
mapStateToProps,
{
hideSplash: () => hideSplash(),
showSplash: () => showSplash(),
setSplashMessage: (message) => setSplashMessage(message)
}
)(RootScene);
RootScene.js
import React, { Component } from 'react';
import Splash from '../views/Splash';
import ErrorBar from '../elements/ErrorBar';
import { View, Text, StyleSheet } from 'react-native';
import api from '../../helpers/api';
class RootScene extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {
api.checkConnectivity().then(response => {
// Hide splash, etc, optimally error states could be handled inside of "api"
});
}
render() {
return (
<View style={styles.rootWrapper}>
<ErrorBar props={this.props.global.errorBar}/>
<Splash/>
</View>
);
}
}
const styles = StyleSheet.create({
rootWrapper: {
flex: 1
}
});
export default RootScene;
api.js
const api = {
checkConnectivity() {
return _buildRequest({ endpoint: '/version' }).then(_makeRequest);
}
};
module.exports = api;
const _buildRequest = (request_data) => {
// ...
};
const _makeRequest = (request_object) => {
// ...
};
I'm aware that my stripped out code above is missing the actions to change the state of the errorBar.
If the way I'm structuring the app is completely nuts, I'm all ears.
Instead of API as "module", try to use it as a middleware. Therefore you will have access to dispatch() on your context.
The idea is dispatching the actions and based on the action your middleware will "decide" to call your api. In case of error the middleware can dispatch your default error action.
This post might help you: http://www.sohamkamani.com/blog/2016/06/05/redux-apis/
You can also use redux-api-middleware: https://github.com/agraboso/redux-api-middleware
You can do this with Thunk Middleware.
http://blog.nojaf.com/2015/12/06/redux-thunk/
https://github.com/gaearon/redux-thunk

Categories

Resources