Outlook add-in is showing an empty html content upon first load - javascript

I am currently building an office 365 add-in on outlook and upon first load the add in is showing an empty html content like so before the whole add in render correctly after couple of seconds
<html>
<head></head>
<body></body>
</html>
First I thought it is from the office.then function shown below and I tried to add a spinner before the .then is called but the loader didn't show, and I couldn't find the reason behind this.
Note that the add-in is developed with react and the following is the index.js page
Also note that the blank page is only showing on outlook for windows installed on a window 10 machine
import "office-ui-fabric-react/dist/css/fabric.min.css";
import App from "./components/App";
import { AppContainer } from "react-hot-loader";
import { initializeIcons } from "#fluentui/react/lib/Icons";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "../../store/store";
import 'react-app-polyfill/ie11';
import Spinner from '#atlaskit/spinner';
/* global AppCpntainer, Component, document, Office, module, React, require */
initializeIcons();
let isOfficeInitialized = false;
const title = "test";
const render = Component => {
ReactDOM.render(
<Provider store={store}>
<AppContainer>
<Component title={title} isOfficeInitialized={isOfficeInitialized} />
</AppContainer>
</Provider>,
document.getElementById("container")
);
};
Office.onReady().then(function () {
isOfficeInitialized = true;
render(App);
});
render(App);
if (module.hot) {
module.hot.accept("./components/App", () => {
const NextApp = require("./components/App").default;
render(NextApp);
});
}

Related

React Loadable and Meteor separate bundle

I am using the following Component with Meteor
https://github.com/CaptainN/npdev-react-loadable
import { Loadable } from 'meteor/npdev:react-loadable';
I create my Loadable component as follows
const HomePageBlog = Loadable({
loading: () => <FullPageLoader />,
loader: () => import('./HomePageBlog'),
});
I have gone through the SSR setup in the docs and it looks something like this
Server index.js
import React from 'react';
import { renderToString, renderToNodeStream } from 'react-dom/server';
import { onPageLoad } from 'meteor/server-render';
import { StaticRouter } from 'react-router';
import { Helmet } from 'react-helmet';
import Loadable from 'react-loadable';
import { ServerStyleSheet } from 'styled-components';
import {
LoadableCaptureProvider,
preloadAllLoadables,
} from 'meteor/npdev:react-loadable';
preloadAllLoadables().then(() => {
onPageLoad(async (sink) => {
const context = {};
const sheet = new ServerStyleSheet();
const loadableHandle = {};
const routes = (await import('../both/routes.js')).default;
const App = (props) => (
<StaticRouter location={props.location} context={context}>
{routes}
</StaticRouter>
);
const modules = [];
// const html = renderToNodeStream((
const html = renderToString(
<LoadableCaptureProvider handle={loadableHandle}>
<App location={sink.request.url} />
</LoadableCaptureProvider>,
);
// we have a list of modules here, hopefully Meteor will allow to add them to bundle
// console.log(modules);
sink.renderIntoElementById('app', html);
sink.appendToBody(loadableHandle.toScriptTag());
const helmet = Helmet.renderStatic();
// console.log(helmet);
sink.appendToHead(helmet.meta.toString());
sink.appendToHead(helmet.title.toString());
sink.appendToHead(helmet.link.toString());
sink.appendToHead(sheet.getStyleTags());
});
});
client index.js
import { Meteor } from 'meteor/meteor';
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, withRouter } from 'react-router-dom';
import { onPageLoad } from 'meteor/server-render';
import { createBrowserHistory } from 'history';
import { preloadLoadables } from 'meteor/npdev:react-loadable';
console.log('hi');
const history = createBrowserHistory();
/**
* If browser back button was used, flush cache
* This ensures that user will always see an accurate, up-to-date view based on their state
* https://stackoverflow.com/questions/8788802/prevent-safari-loading-from-cache-when-back-button-is-clicked
*/
(function () {
window.onpageshow = function (event) {
if (event.persisted) {
window.location.reload();
}
};
})();
onPageLoad(async () => {
const routes = (await import('../both/routes.js')).default;
const App = () => (
<>
<Router history={history}>
<div>{routes}</div>
</Router>
</>
);
preloadLoadables().then(() => {
ReactDOM.hydrate(<App />, document.getElementById('app'));
});
});
What I am trying to determine is what exactly react loadable does. I am wanting to separate my bundle so I can only load code via SSR when it is needed. Right now I have quite a low score on lighthouse for page speed.
The code that I have here works.
But what I expected to happen was have a separate request to grab more js for the loadable component when it is requested. So it's not in the initial bundle. Is this not how this package works.
Could someone one help me me understand this better.
Thanks for any help ahead of time

Why can't you have two AppRegistry.registerComponent lines?

I'm teaching myself React-Native and I came across this strange roadblock. Inside my App.js I'm trying to export a class and then use another file within my App.js which is inside the somePage() function. where I'm calling <Header/> in an attempt for that text to appear on my physical device upon hitting refresh.
It displays <Login/> perfectly, but not what's within the somePage() function. My question is, why?
(I'm using Expo by the way, instead of having an index.ios.js file it's an App.js file that still supports cross platform development).
Here's my App.js file:
import React, { Component } from 'react';
import {AppRegistry} from 'react-native';
import Login from './components/Login';
import Header from './components/Header';
export default class Project extends Component {
render() {
return (
<Login/>
);
}
}
const somePage = () => (
<Header/>
);
AppRegistry.registerComponent('someProject', () => Project);
AppRegistry.registerComponent('someProject', () => somePage);
Here's my Header.js file:
import React from 'react';
import {Text} from 'react-native';
const Header = () => {
return <Text>Testing this out</Text>
}
export default Header;
The concept of react is that a parent component renders child components. You only need to register once because the root component is the parent component of all other components. Any other child or grandchild components you want to render must be descendants of the root component. As a side note, you don't need to have export default in front of the Project component, because you aren't exporting it anywhere: you are registering it below.
To fix your app, you need to place the header component inside the registered root component:
import React, { Component } from 'react';
import {AppRegistry, View } from 'react-native';
import Login from './components/Login';
import Header from './components/Header';
class Project extends Component {
render() {
return (
<View>
<Header/>
<Login/>
</View>
);
}
}
AppRegistry.registerComponent('someProject', () => Project);

How to do server-side rendering in React/redux?

I am new to react/redux I little confused with server side rending in react/redux,
Yes i saw some example on the internet but when i tried with mock api with external server , server side rendering is not working .
cat.js
import React from 'react';
import {render} from 'react-dom';
import {connect} from 'react-redux';
import * as mockApi from '../Actions/mockActions';
class Cat extends React.Component{
componentWillMount(){
this.props.getMockApi();
}
render(){
return(
<div>
Hello Dude
{this.props.mock.data.map((data,i) => {
return <li key={i}>{data.email}</li>
})}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
mock:state.mock
}
};
const mapDispatchToProps = (dispatch) => {
return {
getMockApi:() => dispatch(mockApi.getMockData())
}
};
export default connect(mapStateToProps,mapDispatchToProps)(Cat);
mockActions.js
import axios from 'axios';
import * as types from './actionTypes';
export function getMockData() {
return dispatch => {
return axios.get('http://jsonplaceholder.typicode.com/users').then(response => {
dispatch(setThisData(response.data))
})
}
}
export function setThisData(data) {
return {
type:types.MOCK_API,
payload:data
}
}
App.js
import React from 'react';
import {render} from 'react-dom';
import Cat from './components/cat'
import {Provider} from 'react-redux';
import configureStore from './Store/configureStore';
import { createStore ,applyMiddleware,compose} from 'redux';
import counterApp from './Reducers'
import thunk from 'redux-thunk';
if(typeof window !== 'undefined'){
// Grab the state from a global variable injected into the server-generated HTML
const preloadedState = window.__PRELOADED_STATE__
// Allow the passed state to be garbage-collected
delete window.__PRELOADED_STATE__
const store = createStore(counterApp, preloadedState, compose(applyMiddleware(thunk)))
render(
<Provider store={store} >
<Cat/>
</Provider>
,
document.getElementById('app')
)
}
devServer.js
import express from 'express';
import path from 'path';
import webpack from 'webpack';
import webpackMiddleware from 'webpack-dev-middleware'
import webpackHotMidleware from 'webpack-hot-middleware';
import bodyParser from 'body-parser';
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux';
import counterApp from '../../src/client/ReduxServer/Reducers';
import App from '../../src/client/ReduxServer/components/cat';
import { renderToString } from 'react-dom/server'
import webpackConfig from '../../webpack.config.dev';
let app = express();
app.use(bodyParser.json());
app.use(express.static('public'))
const compiler = webpack(webpackConfig);
app.use(webpackMiddleware(compiler, {
hot: true,
publicPath: webpackConfig.output.publicPath,
noInfo: true
}));
app.use(webpackHotMidleware(compiler));
// app.get('/*', (req, res) => {
// res.sendFile(path.join(__dirname, '../../index.html'))
// });
//Redux Start
app.use(handleRender);
function handleRender(req,res) {
const store = createStore(counterApp);
const html = renderToString(
<Provider store={store} >
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
}
function renderFullPage(html, preloadedState) {
console.log(preloadedState)
return `
<!doctype html>
<html>
<head>
<title>Redux Universal Example</title>
</head>
<body>
<div id="app">${html}</div>
<script>
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
</script>
<script src="bundle.js"></script>
</body>
</html>
`
}
//Redux Ends
app.listen(3000, () => {
console.log('Listening')
});
Right now this will only server render the hello dude but not the mock Api call data .I know that missed to fetch the data from server side but the point is what will i do If ihave to render a two components and that component has 5 api reuqest ,And how to fecth the correct api Request
Right Now My client Side Prefecthed state will look like this
window.__PRELOADED_STATE__ = {"mock":{"data":[]}}
Ok, to make this clear, you've created the code to handle server rendering. However, it doesn't load the data that is supposed to be fetched right?
You've done the first step, great! The next step is to load the actual dynamic data to the store. Let's look at this code here
function handleRender(req,res) {
const store = createStore(counterApp);
const html = renderToString(
<Provider store={store} >
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
}
What happened is that you created a store. The store is used to render the html into a string. Then you get the store state and put it into preloadedState.
This is great accept that renderToString will not call this.props.getMockApi(); as you would expect.
Instead, you have to fetch the state before you call renderToString();
In this case, what you could do is as following. (Note that this is just an example, you probably want to use something more general in production, especially if you use something like react-router.)
import * as mockApi from '../Actions/mockActions';
function handleRender(req, res) {
const store = createStore(counterApp);
store.dispatch(mockApi.getMockData())
// And since you used redux-thunk, it should return a promise
.then(() => {
const html = renderToString(
<Provider store={store}>
<App/>
</Provider>
)
const preloadedState = store.getState();
// Send the rendered page back to the client
res.send(renderFullPage(html, preloadedState))
});
}
Simple isn't it? ;D, nah just joking. This is one part of react where there's not really an exact solution to the problem yet.
Personally, if I had the choice to go back in time, I'd tell myself to learn other stuff other than server rendering. There are other techniques such as code splitting, lazy loading, etc that I could've used instead. With server rendering, if the javascript arrives long after the user has seen the initial page, they might get frustrated by other things that require js. For example in my case, some links are not working, some buttons don't do anything, etc.
I'm not saying that server rendering is not good. It's an interesting technique, just that there are other techniques that are more beneficial to learn first (Oh, and server rendering basically locks you to use nodejs for your backend). Good luck to you :)

ExNavigation throws an error saying initialRoute is not defined

Hi I'm new to Expo but I've been having a hard time trying to run my code. I'm stuck at having the error: You must specify initialRoute or initialStack to initialize this StackNavigation even though I already set it up.
Here's my main.js
import Expo from 'expo'
import React from 'react'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import {
NavigationProvider,
StackNavigation,
} from '#expo/ex-navigation'
import RootReducer from './src/reducers'
import Router from './src/navigation/Router'
const store = createStore(RootReducer)
const App = () => (
<Provider store={store}>
<NavigationProvider router={Router}>
<StackNavigation intitialRoute={Router.getRoute('splash')} />
</NavigationProvider>
</Provider>
)
Expo.registerRootComponent(App)
Here's my Router.js
import { createRouter } from '#expo/ex-navigation'
// Screens
import SplashScreen from '../screens/SplashScreen'
import LoginScreen from '../screens/LoginScreen'
const Router = createRouter(() => ({
splash: () => SplashScreen,
login: () => LoginScreen,
}))
export default Router
What seems to be the problem at my setup? I just followed the example on ExNavigation.
Here's my example on Sketch but can't make it run but will leave the link for the full code.
You have a typo in the prop's name in this part of the code
<StackNavigation intitialRoute={Router.getRoute('splash')} />
It is initialRoute instead of intitialRoute.

React-Router is not display anything and also no error

I am try to make inventory list app with react and redux , but I have a small problem with need some understanding about it .
I know we have few way to create react components and try to get as much as possible all information , but still not sure why this issue is happen .
I have app.js which is my all React-Router set there
console.log('The application has been start...');
import React from 'react'
import { render } from 'react-dom'
import { IndexPage } from './modules/IndexPage'
import { AddItemForm } from './modules/ui/AddItemForm'
import { PageNotFound } from './modules/PageNotFound'
import { Router, Route, hashHistory } from 'react-router'
import routes from './routes'
window.React = React
render(
<Router history={hashHistory}>
<Route path='/' component={IndexPage}/>
<Route path='/add' component={AddItemForm}/>
<Route path='*' component={PageNotFound}/>
</Router>,
document.getElementById('react-container')
)
for IndexPage and PageNotFound the router display and render correctly , But for add , is display blank page with no error .
import { PropTypes } from 'react'
const AddItemForm = ({ onNewItem=f=>f, router}) => {
//const AddItemForm = () => {
let _itemName
const submit = e => {
e.preventDefault()
onNewItem({
itemName: _itemName.value,
itemCount: 1
})
router.push('/')
_itemName.value = ''
}
return (
<form onSubmit={submit} >something
<label htmlFor="item-name"> Name Item</label>
<input id="item-name" type="text" ref={(input) => _itemName = input } />
<button>Add Item</button>
</form>
)
}
AddItemForm.propTypes = {
onNewItem: PropTypes.func,
router: PropTypes.object
}
export default AddItemForm
in order to make sure there is something wrong with React-Router or the components which I made I change the AddItemForm with below code
export const AddItemForm = () =>
<div>
<h1>Oops ! - The page is working!</h1>
</div>
which start working normally which shows something wrong with my components but I am not able to understand what is the main issue. Please give me hit or point where is the issue or what is the different ?
Here is link of github for full source of application so far
https://github.com/msc/Inventory-List
I tried to reproduce this issue. What I figured out is that you also need to import React for your component along with PropTypes.
So try importing React
import React, { PropTypes } from 'react'
in your AddItemForm component. Then worked for me.

Categories