Using higher order components with Redux containers - javascript

First, some context.
I'm using Redux to manage authentication state of my app and have Auth as a Redux container (or smart component).
I've created a wrapper (a higher-order component) that takes Auth and returns it:
export default function AuthWrapper(WrappedComponent) {
class Auth extends Component {
... <Auth stuff here> ...
}
return connect(mapStateToProps, mapDispatchToProps)(Auth);
}
It seems to me that in order to use the wrapper, I just need to invoke it with a component I want to have behind my auth. For example, let's say I'm authenticating a component called UserPage with the wrapper, à la:
const AuthenticatedUserPage = AuthWappper(UserPage)
However, when I use the wrapper like this, React isn't happy with me. I get the following error:
Warning: AuthenticatedApp(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
My best guess is that it doesn't like the connect-ified component that Redux will create when I return it from AuthWrapper... which leads me to my question:
Does React support higher-order components when those components create Redux containers? And if so, why would React be throwing this error?

Here's my two cents. I think the error is occurring elsewhere.
According to this simplified version of the connect function in react-redux, the connect function is simply returning another react component. So in your case, you're returning a component, wrapped inside another component, which is still valid. A container is basically a component.
Read https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e for a better understanding of the connect function.
I also tried the following in my own application and it worked.
import Layout from '../components/Layout'
//Do some other imports and stuff
function wrapper(Layout) {
return connect(null, mapDispatchToProps)(Layout);
}
export default wrapper()
Like the error states, you might just simply be returning an invalid component somewhere in your app. Your app might be throwing the error because you're not wrapping a return call in parentheses on your render method.

Related

Where do generic fetch() calls go in React / Redux?

By generic I mean a fetch() call is not tied to a single component.
Where should I put this fetch() call? Currently I have it in the top level React component. I simply return null and it does not render anything but it does run and populate redux.
This method seems a bit hacky and was wondering if React provides a way, or is there a common design pattern, to say fetch data that is not tied to a single component.
In general how should I do this?
I would like to write a simple JavaScript function, and then disptach() the data to redux, but React will not let me use dispatch inside a non-rendered JS function.
In general how does one write fetch() when the fetch() is not tied to a single component, and also when this fetch() needs to use dispatch().
// hacky way
import React from 'react';
import { useDispatch } from 'react-redux';
export default () => {
const dispatch = useDispatch();
// run fetch() here
return null;
};

ReactJS' Higher Order Components error: "Unknown props"

Yesterday, I was reading the React documentation on higher order components and I was trying to use some of the examples that they have. But, for me, it isn't working.
Here is a simple HOC I created just to wrap another component and see how this works. But since the very beginning, it never worked.
import React, { Component } from 'react';
export default function (enhacedComponent) {
class Authenticate extends Component {
constructor(props) {
super(props);
}
render() {
return <enhacedComponent {...this.props} />;
}
}
return Authenticate;
}
It always returns me this error:
Warning: Unknown props `location`, `params`, `route`, `router`, `routeParams`, `routes` on <enhacedComponent> tag. Remove these props from the element.
When I check the HTML elements part in the console, I find that the actual value this HOC returns is <enhacedComponent></enhacedComponent>. So the wrapped component never got out!
So, in the end, the wrapped component never returns. Just a JSX version of what should be the argument of the HOC.
I think that since JSX is just a another syntax and the unique way to pass plain JavaScript is using {}, I tried to do this, to no success:
<{enhancedComponent} {...this.props }/>
I really don't know what to do or what I am doing wrong.
I'm using this HOC reference. I'm using Webpack 2 with webpack-dev-server as tools on Windows 10.
React thinks you're trying to pass these props to a DOM element and not a react component, which will give you the unknown props error. React interprets lower camel case as a DOM element, so enhacedComponent should be EnhacedComponent.
More info here:
https://facebook.github.io/react/warnings/unknown-prop.html

How to register page views in a React / Redux app

Edit
Actually, I'm fairly certain that this is because my action creator doesn't return an action and the first property of an action is supposed to be a type and that is therefore undefined...
I have a React/Redux SPA that I want to register page views on with a custom analytics engine (ie, not Google Analytics). I'm trying to register page views.
So I have attempted to do this by setting lifecycle hooks in React to fire a Redux action:
class ConfirmationPage extends Component {
...
componentWillMount() {
this.props.registerPageVisited('confirmation');
}
}
However, I receive a Cannot read property 'type' of undefined error presumably because I am modifying the state via the props. Looking at the stack trace, it brings me to that hook. However, I've tried other hooks such as componentDidMount and even componentWillUnMount and I get the same error.
For context, my action creator is this:
export function registerPageVisited(page) {
DB.child('visits')
.child(store.getState().visit)
.update({ [page]: true });
}
where the DB is a firebase reference.
So, how should I keep track of page views?
Could you provide code where you instantiate ConfirmationPage component?
In the componentWillMount function what you are actually doing is calling registerPageVisited function which should be passed as props which should look something like: <ComponentPage registerPageVisited={registerPageVisited} />
As you are suspecting, since your registerPageVisited does not return action object it is not action creator. If you will make it an action creator, then you should use mapDispatchToProps function to use it like any other props as sen in your example.
An alternate to this is have a different object listen to notifications from the store and update the DB based on the changes it's seeing. That will isolate any metrics collection from your view hierarchy.
You'd have to replicate some logic that determines which React components are getting shown into that object, but you could pull that into a separate class and use it in both places.
If you're using react-router, there might be hooks that fire when the active route changes. However, I haven't investigated that.

How to use custom functions in a React class after it was rendered?

Let's say that I create additional functions in my React components, something like this:
class Cart extends React.Component {
render() {
return <div id="cart">My Cart Items</div>
}
getItem(id) {
// magically return the item
}
addItem(id, name, type, price){
// you get the idea
}
}
Now, what is the best way to access getItem function from outside the class? For example, something that can be used as window.Cart.getItem. It seems to me that these functions are part of the prototype (non-initialized) of nodeName property of what ReactDOM.render returns.
What is the correct way for this? Thanks.
That's not really how react is supposed to be used.
What you are trying to do looks like MVC, and react is a completely different thing.
a React component like your <Cart> receives props from its parent (could be the root reactDOM.render() or another react component)
a React component may have internal and private method to retrieve additional data from elsewhere (but NOT from other react components)
with these inputs (and only these inputs), the component knows what and how to render itself and possibly also children components to the DOM
the stuff the component (or its children components) render may include interaction handlers (e.g. remove from cart button like `
these interactions could fire an internal method inside the component
and the internal method could call a method in a parent component, if it was passed down as a prop.
or call some remote function to remove item from cart on server side.
In react terms, the <Cart> component is not the owner of the cart contents. It gets them as props, or retrieves them from somewhere else.
React really wants you to ONLY update the cart by passing a new set of props to the component to render.
You could try to shortcut react design by trying to expose any methods from the component to the outside world, but this goes against react design principles.
React has a one way data flow: a component gets props, and renders.
For reference, I can recommend this page on react principles.
You can make a reference to the instance :)
<Cart ref={function(instance){ window.CartInstance = instance }} />
window.CartInstance.getItem(foo);
ES6
<Cart ref={(instance)=>window.CartInstance = instance} />
window.CartInstance.getItem(foo);

Sharing global/singleton data in react app

I'm rewriting a small app to try and better understand React. I'm trying to determine the "correct"/most efficient method of sharing "singleton" data - for example, a user who's been properly authenticated upon login.
Right now the parent "application" component has a user property in its state, which I pass to child components as a prop:
<Toolbar user={this.state.user} />
<RouteHandler user={this.state.user}/>
(I'm using react-router). This works, and in read-only cases like this, isn't terrible. However, my actual login form component (which is a route, and would be inside RouteHandler), needs some way to "set" the new user data, so I also need to pass in some callback:
<RouteHandler onAuthenticated={this.setUser} user={this.state.user}/>
Not a big problem, except for the fact that now this method is available to every "route" handled by RouteHandler.
I've been reading up and it seems like the only alternative is an EventEmitter or Dispatch-style system.
Is there a better way I'm missing? Is an event emitter/dispatcher system worth using when there's really only one or two uses in an app this small?
React Context provides a way to pass data through the component tree without having to pass props down manually at every level. With context, every component nested under a Provider has access to the data, but you need to explicitly read the value.
I recommend using React Hooks with useContext. One way to do this would be to set the value of the context to be an object with setter and getter functions.
import React, { useState, useContext } from "react"
export const UserContext = React.createContext({}); //Initialise
//Wrapper with getter and setter
const App = () => {
const [user, setUser] = useState();
const value = {user, setUser}
return (
<div>
<UserContext.Provider value={value}>
<RouteHandler/>
<AnotherComponent/>
</UserContext>
<ComponentWithoutAccessToUserContext/>
</div>
)
}
const RouteHandler = (props)=> {
const { user, setUser } = useContext(UserContext)
// This component now has access to read 'user' and modify it with 'setUser'
}
const AnotherComponent = () => {
return (<div>Component which could be get access to the UserContext</div>)
}
For singleton - you can just create separate module for user service and import it into module where you define components that need it it.
Other quite similar, but more powerful option, is to use DI container - define your react components as a services in DI container, with dependencies to other services like one for user data. This would be more suitable for universal(isomorphic) app - because, you will be able to easily replace dependencies with specific implementations, or for case when you need to create separate instances for separate scopes(like for user sessions server-side).
Also if using this approach, I would recommend to separate pure react components from logic - you can create separate pure component that receives all data, and callbacks as a props, and than create HoC component in DI container that will wrap it and will pass needed data and callbacks.
If you need DI container - there is a plenty of them, but I will recommend to look at angular 2 di container, or if you would like something simpler - below I referenced my project, it has very simple but yet powerful DI inspired by angular 2 DI(it is easy to pull from that project - just one file + test)).
About notifying components about changes, and organising async logic - you still will need something like EventEmitter to notify components about changes, and you will need to write life cycle callbacks for components to subscribe/unsubscribe from updates… You can do this by hand or creating mixin or HoC to shorten that.
But from my perspective, there is better approach - try reactive programming, and RxJS in particular. It plays very well with react.
If you are interested about options connecting Rx with React - take a look at gist https://gist.github.com/zxbodya/20c63681d45a049df3fc, also it can be helpful about implementing HoC component with subscription to EventEmitter mentioned above.
I have a project that is intended for creating isomorphic(rendered server side, and than same html reused client side) widgets with react.
It has DI container to pass dependencies, and it uses RxJS to manage async logic:
https://github.com/zxbodya/reactive-widgets
One way is to subscribe to an Observable emitted from your data model.
Router.run(routes, Handler =>
Model.subject.subscribe(appState =>
React.render(
<Handler {...appState}/>,
document.getElementById('app')
)
)
);
...appState being the data coming from observable (in this case model), making these your props so you can then feed them to the app like below
<RouteHandler {...this.props} />
and any child component can pick them up with this.props
the answer is more complex that this but if you look at RxJS+React you will get a full working examples of simple data flows

Categories

Resources