How to use mdx-deck with react-redux? - react-redux

I'm making web slides using this awesome library called mdx-deck. I've written my own React components for my slide deck. Now I want to add react-redux to my slide deck so that all components share a common store. How can I do this?
I know I should use the <Provider /> component from react-redux, so I've been reading about mdx-deck custom provider and MDXProvider, but I haven't been able to see the <Provider /> in my DOM.
I'm using yarn start to serve my slide deck:
// package.json
{
"scripts": {
"start": "mdx-deck concat-slides.js",
}
}
The concat-slides.js script concatenates two slide decks that I have:
// concat-slides.js
import { slides as cv } from 'src/slides/cv.mdx';
import { slides as bootstrap } from 'src/slides/bootstrap.mdx';
import { distillLite } from 'src/slides/theme/index';
export const themes = [distillLite];
export const slides = [...cv, ...bootstrap]
And here's my attempt to add a custom provider (but I don't see a provider in my DOM tree?):
// src/slides/theme/index.tsx
import React from 'react'
import { createStore } from "redux";
import { Provider as ReduxProvider } from "react-redux";
export const store = createStore(() => {});
const Provider = (props:any) => (
<ReduxProvider store={store}>
{props.children}
</ReduxProvider>
)
export const distillLite = {
Provider,
}

Related

How can I edit/Update the launch screen text in react-native app(Xcode)

I need to update my app name in launch/splash screen.
How can I update my launch screens name?
Can any one give the steps how to update the launch screen text?
I have Changed the LaunchScreen.xib in Xcode
<string key="text">Renamed </string>
You should :
1) Change class in index.js file from App.js to whichever class you want
import {AppRegistry} from 'react-native';
import routes from './src/Routes';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => routes);
2) Create Route file
import React from 'react'
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import Splash from './Splash'
import Calc from './Calc'
const AppContainer = createAppContainer(
createSwitchNavigator(
{
Splash: Splash,
Calc: Calc,
},
{
initialRouteName: 'Splash',
},
),
)
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
3) Create desired splash screen
import React, { Component } from 'react';
import {
Text,
Image,
View
} from 'react-native';
import Style from './Style'
class Splash extends Component {
componentDidMount() {
setTimeout(() => {
this.props.navigation.navigate('Calc')
}, 1500)
}
render() {
return (
<View style={Style.splashContainer}>
<Image
source={{ uri: 'https://cdn2.iconfinder.com/data/icons/ios7-inspired-mac-icon-set/1024/Calculator_5122x.png' }}
style={Style.splashImage}
/>
<Text style={Style.splashText}>Calculator</Text>
</View>
)
}
}
export default Splash
In this last file you can design your splash screen however you want
That's how you can create custom launcher screen and splash screen in react native
Thank you

Images only load when directly required and are inconsistent when required through another module

What is consistent
[Edit: No longer consistent for whatever reason]
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
export default class OwnMarker extends React.Component {
render() {
return(
<Image
style={STYLE.marker}
source={require('../../../../resources/images/marker.png')}
resizeMethod="resize"
/>
);
}
}
This results in a consistently working solution.
What is inconsistent
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
//Note import
import Images from '../../../../resources/Images';
export default class OwnMarker extends React.Component {
render() {
return(
<Image
style={STYLE.marker}
source={Images.own_marker}
resizeMethod="resize"
/>
);
}
}
And the imported object looks like this:
//Images.js
module.exports = {
own_marker: require('./images/marker.png'),
}
This results in an inconsistent solution
Sometimes these images load and sometimes they don't. It might only render the image for 3 of the markers, none of them, all of them, 7 or them, or whatever other possibility between none and all of the markers loading.
How can I make this work from the import statement and why is it currently inconsistent?
It's hard to sleep at night with a solution like this, but this is the only one that has worked for me.
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
import Images from '../../../../resources/Images';
export default class OwnMarker extends React.Component {
constructor(props){
this.state = {
// HACK
initialRender: true
}
}
render() {
return(
<Image
style={STYLE.marker}
source={Images.own_marker}
resizeMethod="resize"
// HACK
onLayout={() => this.setState({ initialRender: false })}
key={`${this.state.initialRender}`}
/>
);
}
}

undefined this.props.nnavigation.navigate('screen'), how to fix?

Im trying to use Drawer navigation. I have header.js components which has a hamburger icon to open the left navigation menu
//Header.js
import React from 'react';
import { Text, View, Image, TouchableHighlight} from 'react-native';
import { DrawerNavigator } from 'react-navigation';
// Make a component
const Header = (props, {navigate}) => {
const { textStyle, viewStyle, imgstyle } = styles;
return (
<View style={viewStyle}>
<TouchableHighlight onPress={()=> this.props.navigation.navigate('DrawerToggle')}> //here the error is coming
<Image style={{width:40, height:40}}
source={require('./../media/hamburger.png')}
/>
</TouchableHighlight>
<Text style={textStyle}>{props.headerText}</Text>
</View>
);
};
export default Header;
Here is App.js
const MyApp = DrawerNavigator({
Login: {
screen: Login,
},
Signup: {
screen: Signup,
},
});
export default class App extends React.Component {
render() {
return <MyApp />;
}
}
Here is one of the comoponent Login.js
import React, { Component } from 'react';
import {
Text, View, StyleSheet
} from 'react-native';
import Header from './Header';
export default class Login extends Component{
static navigationOptions = {
drawerLabel: 'Login',
};
render(){
return(
<View >
<Header headerText={'Login'}/>
<Text>Login Screen</Text>
</View>
);
}
}
I am not sure what I am missing to fix this. Please check my code.
EDITED:
I tried without "this" to but no luck. I am checking everywhere in the internet but couldn't find the exact solution or I couldn't get a solution to my mind. Can someone who is good with react native respond on this, Please?
The problem is - you use this keyword in a stateless component: this.props.navigation.navigate('DrawerToggle').
Try just props.navigation.navigate('DrawerToggle') since this is not pointing to the Header component instance in your case.
this in a stateless component is "inherited" from the calling scope (since stateless component is an "arrow" function) and likely points to window or global.

Mocha-Chai throws "navigator not defined" due to React-router component

I am writing a test for a React component that uses react-router. I am using Mocha with Chai and Chai-jQuery.
My tests work fine, until I import a component from react-router into a component (e.g. Link). At this point, I get the following error:
ReferenceError: navigator is not defined
at Object.supportsHistory (/Users/nico/google-drive/code/agathos/client/node_modules/history/lib/DOMUtils.js:61:12)
I used to get a similar error with react-bootstrap until I updated to react-bootstrap v0.29.3. I have the most recent version of react-router v2.4.0 (and history v2.1.1). But the problem persists.
The only solution I have found is to change node_modules/history/lib/DOMUtils: navigator into window.navigator. This is a hack though, and not a good solution.
I think the problem is with react-router, but I don't have a solution.
Just in case, here is my test-helper.js.
import jquery from 'jquery';
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-addons-test-utils';
import jsdom from 'jsdom';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from './reducers';
// set up a testing environment to run like a browser in the command line
// create a fake browser and html doc
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.defaultView;
// prevent jquery defaulting to the dom by giving it access to the global.window
const $ = jquery(window);
// build renderComponent function that should render a React class
function renderComponent(ComponentClass, props = {}, appState = {}) {
const componentInstance = TestUtils.renderIntoDocument(
<Provider store={createStore(reducers, appState)}>
<ComponentClass {...props} />
</Provider>
);
// produces html
return $(ReactDOM.findDOMNode(componentInstance));
}
//build a helper for simulating events
// $.fn allows you to add a custom function to your jquery library
$.fn.simulate = function(eventName, value) {
if (value) {
// `this` allows you to access the object appended to
// `val()` is a jquery function that sets the value of selected html element
this.val(value);
}
// the [] are object method selectors, which allow you to access e.g. Simulate.change
TestUtils.Simulate[eventName](this[0]);
};
// set up chai jquery
chaiJquery(chai, chai.util, $);
export {renderComponent, expect};
It seems that react-router assumes navigator is in the global scope.
To resolve this error, you should add navigator to the global scope in your test setup phase:
global.navigator = {
userAgent: 'node.js'
};

Animated page transitions in react

The past couple of weeks I've been working on an app using React. So far everything is working fine, but now I want to add some transitions to it. These transitions are a bit more complex than any examples I managed to find.
I've got 2 pages, an overview and a detail page which I'd like to transition between.
I'm using react-router to manage my routes:
<Route path='/' component={CoreLayout}>
<Route path=':pageSlug' component={Overview} />
<Route path=':pageSlug/:detailSlug' component={DetailView} />
</Route>
Overview looks like this:
Detailview looks like this:
The idea of the transition is that you click on one of the elements of the Overview. This element which has been clicked moves towards the position it should have on the detailView. The transition should be initiated by a route change (I think) and should also be able to happen in reverse.
I've already tried using ReactTransitionGroup on the Layout, which has a render method which looks like this:
render () {
return (
<div className='layout'>
<ReactTransitionGroup>
React.cloneElement(this.props.children, { key: this.props.location.pathname })
</ReactTransitionGroup>
</div>
)
}
This will give the child component the ability to receive the special lifecycle hooks. But I'd like to access the child components somehow during these hooks and still keep doing things the React way.
Could someone point me in the right direction for the next step to take? Or maybe point me to an example which I may have missed somewhere? In previous projects I used Ember together with liquid fire to get these kinds of transitions, is there maybe something like this for React?
I'm using react/react-redux/react-router/react-router-redux.
Edit: Added a working example
https://lab.award.is/react-shared-element-transition-example/
(Some issues in Safari for macOS for me)
The idea is to have the elements to be animated wrapped in a container that stores its positions when mounted. I created a simple React Component called SharedElement that does exactly this.
So step by step for your example (Overview view and Detailview):
The Overview view gets mounted. Each item (the squares) inside the Overview is wrapped in the SharedElement with a unique ID (for example item-0, item-1 etc). The SharedElement component stores the position for each item in a static Store variable (by the ID you gave them).
You navigate to the Detailview. The Detailview is wrapped into another SharedElement that has the same ID as the item you clicked on, so for example item-4.
Now this time, the SharedElement sees that an item with the same ID is already registered in its store. It will clone the new element, apply the old elements position to it (the one from the Detailview) and animates to the new position (I did it using GSAP). When the animation has completed, it overwrites the new position for the item in the store.
Using this technique, it's actually independent from React Router (no special lifecycle methods but componentDidMount) and it will even work when landing on the Overview page first and navigating to the Overview page.
I will share my implementation with you, but be aware that it has some known bugs. E.g. you have to deal with z-indeces and overflows yourself; and it doesn't handle unregistering element positions from the store yet. I'm pretty sure if someone can spend some time on this, you can make a great little plugin out of it.
The implementation:
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Overview from './Overview'
import DetailView from './DetailView'
import "./index.css";
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
const routes = (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Overview} />
<Route path="detail/:id" component={DetailView} />
</Route>
</Router>
)
ReactDOM.render(
routes,
document.getElementById('root')
);
App.js
import React, {Component} from "react"
import "./App.css"
export default class App extends Component {
render() {
return (
<div className="App">
{this.props.children}
</div>
)
}
}
Overview.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './Overview.css'
import items from './items' // Simple array containing objects like {title: '...'}
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class Overview extends Component {
showDetail = (e, id) => {
e.preventDefault()
hashHistory.push(`/detail/${id}`)
}
render() {
return (
<div className="Overview">
{items.map((item, index) => {
return (
<div className="ItemOuter" key={`outer-${index}`}>
<SharedElement id={`item-${index}`}>
<a
className="Item"
key={`overview-item`}
onClick={e => this.showDetail(e, index + 1)}
>
<div className="Item-image">
<img src={require(`./img/${index + 1}.jpg`)} alt=""/>
</div>
{item.title}
</a>
</SharedElement>
</div>
)
})}
</div>
)
}
}
DetailView.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './DetailItem.css'
import items from './items'
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class DetailView extends Component {
getItem = () => {
return items[this.props.params.id - 1]
}
showHome = e => {
e.preventDefault()
hashHistory.push(`/`)
}
render() {
const item = this.getItem()
return (
<div className="DetailItemOuter">
<SharedElement id={`item-${this.props.params.id - 1}`}>
<div className="DetailItem" onClick={this.showHome}>
<div className="DetailItem-image">
<img src={require(`./img/${this.props.params.id}.jpg`)} alt=""/>
</div>
Full title: {item.title}
</div>
</SharedElement>
</div>
)
}
}
SharedElement.js
import React, { Component, PropTypes, cloneElement } from 'react'
import { findDOMNode } from 'react-dom'
import TweenMax, { Power3 } from 'gsap'
export default class SharedElement extends Component {
static Store = {}
element = null
static props = {
id: PropTypes.string.isRequired,
children: PropTypes.element.isRequired,
duration: PropTypes.number,
delay: PropTypes.number,
keepPosition: PropTypes.bool,
}
static defaultProps = {
duration: 0.4,
delay: 0,
keepPosition: false,
}
storeNewPosition(rect) {
SharedElement.Store[this.props.id] = rect
}
componentDidMount() {
// Figure out the position of the new element
const node = findDOMNode(this.element)
const rect = node.getBoundingClientRect()
const newPosition = {
width: rect.width,
height: rect.height,
}
if ( ! this.props.keepPosition) {
newPosition.top = rect.top
newPosition.left = rect.left
}
if (SharedElement.Store.hasOwnProperty(this.props.id)) {
// Element was already mounted, animate
const oldPosition = SharedElement.Store[this.props.id]
TweenMax.fromTo(node, this.props.duration, oldPosition, {
...newPosition,
ease: Power3.easeInOut,
delay: this.props.delay,
onComplete: () => this.storeNewPosition(newPosition)
})
}
else {
setTimeout(() => { // Fix for 'rect' having wrong dimensions
this.storeNewPosition(newPosition)
}, 50)
}
}
render() {
return cloneElement(this.props.children, {
...this.props.children.props,
ref: element => this.element = element,
style: {...this.props.children.props.style || {}, position: 'absolute'},
})
}
}
I actually had a similar problem, where I had a search bar and wanted it to move and wrap to a different size and place on a specific route (like a general search in the navbar and a dedicated search page). For that reason, I created a component very similar to SharedElement above.
The component expects as props, a singularKey and a singularPriority and than you render the component in serval places, but the component will only render the highest priority and animate to it.
The component is on npm as react-singular-compoment
And here is the GitHub page for the docs.

Resources