Use redux connect function passing props.children as React element - reactjs

I need a component that starts polling for data from an API as soon as it gets mounted and it stops when unmounted. The data must be available for a child component.
This is my current implementation skeleton
class External extends Component {
render() {
const ConnectedPoller = connect(/*cut*/)(Poller)
return <ConnectedPoller {...this.props}>
{this.props.children}
</ConnectedPoller>
}
}
class Poller extends Component {
componentDidMount() {
this.model.startPolling();
}
componentWillUnmount() {
this.model.stopPolling();
}
render() {
const children = React.Children.map(this.props.children, child => {
return React.cloneElement(child, {...this.props});
});
return children;
}
}
There are a few components that need these data, I can use my poller as their parent in this way
<External>
<ComponentRequiringData {...this.props}>
</External>
This is working, but I would like to merge External into Poller. The problem is that I cannot find a way to do this
const ConnectedPoller = connect(/*cut*/)(this.props.children)
The connect function complains when I pass props.children instead of a "real" react element. Any suggestion?
DISCLAIMER: I don't poll at an ancestor level because it's a heavy resource-consuming task for the backend and I need those data only in a few components that are used rarely and are never rendered together in the same page.

The only reason to use redux' connect function, is if you want to connect to the state manangement - either get the current state, or dispatch an action. Since you aren't doing any of these here, there is no reason to use connect. Also, while it might work, there is no reason to use connect inside a rendering function. This will recreate the commponent over and over again for no reason. I guess you are doing it because you want to pass the component with props, but connect can receive props from a parent element as a second argument.

Related

Where to Put Code that should run First ReactJs + Mobx State Tree

I have some code that grabs the users ipAddres. I do this right now in my componentDidMount in my app.js
async componentDidMount() {
await eventTrackingStore.getIpAddress();
}
So I did it in my app.js as it is my root component and I only want to set this once. This works fine if the user starts from the home page and navigates through the site.
However some pages can be loaded directly(ie you type in the url in your browser and it goes straight to that page).
Since the react lifecycle starts with most immediate component, which calls a method that expects the ipAddress code to be set but it does not get set till it hits the app.js
Now I could put the above code in each method but that gets tedious. Is there some sort of method in reactjs, or mbox or mbox state tree that would fire first?
If you use mobx-state-tree and you have a global store, then that global store can make the API call in the afterCreate method
const AppModel = types.model({
ips: types.array(types.string)
}).actions(self => ({
afterCreate() {
flow(function*() {
const ips = yield eventTrackingStore.getIpAddress();
self.setIps(ips)
})()
},
setIps(ips: string[]) {
self.ips = ips
}
}))
OR
The same thing you can do in a wrapped react component that wrappes every page of your app.
class App extends React.Component {
componentDidMount() {
eventTrackingStore.getIpAddress().then(res => {
// set the ips into a store or any logic you want in order to pass them down to children
})
}
render() {
return this.props.children
}
}
I can think of two solutions:
You can use react context
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Use context to share the store between all components and if the data is not loaded, initialize loading right there in that nested component.
If the data is already there then just take it,
getIpAddress method should return a promise, so in case when data is already there it will be immediately resolved.

converting react classes to functions with redux

I'm still new to react/redux, after getting something like this to function
User.js
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
return (
<Profile />
)
}
export default connect(null, {fetchUser})(User);
Profile.js
class Profile extends React.Component {
render() {
const { user } = this.props
return (
<h1>{user.profile.name}</h1>
)
}
const mapStateToProps = state => ({
user: state.store.user
});
export default connect(mapStateToProps, {})(Profile)
actions.js
export const fetchUser = (.....) => dispatch => {
fetch()
.....
}
reducers.js
case FETCH_USER:
return {
...state,
user: action.payload.user
};
As I understand it, the User component calls an action (fetchUser) from connect on componentWillMount(). That action calls an api, gets the data and the reducer adds that to the store within the state. The Profile component can then use connect to map the data from fetchUser in the store and display that data.
After reading some tutorials including https://github.com/reactjs/redux/blob/master/docs/basics/UsageWithReact.md
It looks like things can be simplified a bit without using classes.
If I were to change the User and Profile components to a more functional way, how would I do it?
eg.
const User = () => {
return (
<Profile />
)
}
how do I dispatch the fetchUser action and how do I simulate it to be called with the flow of componentWillMount()?
or am I just over complicating things?
There is also a way to support lifecycle methods in functional components.
https://www.npmjs.com/package/react-pure-lifecycle
import React from 'react';
import lifecycle from 'react-pure-lifecycle';
// create your lifecycle methods
const componentDidMount = (props) => {
console.log('I mounted! Here are my props: ', props);
};
// make them properties on a standard object
const methods = {
componentDidMount
};
const FunctionalComponent = ({children}) => {
return (
<div>
{children}
</div>
);
};
// decorate the component
export default lifecycle(methods)(FunctionalComponent);
I think you should keep using statefull components with redux...
https://medium.com/#antonkorzunov/2-things-about-purecomponent-you-probable-should-know-b04844a90d4
Redux connect — is a PureComponent.
Yes — a very important thing, a HoC for a molecule is a pure one. And works even inside other pure components. And gets store from a current context.
Same is working, for example, for styled-component — you can wrap it with PureComponent, but it will still react to Theme changes.
Solution is simple — bypass logic, use old school events bus, subcribe, wait and emit events.
Styled-componets:
componentWillMount() {
// subscribe to the event emitter. This
// is necessary due to pure components blocking
// context updates, this circumvents
// that by updating when an event is emitted.
const subscribe = this.context[CHANNEL];
this.unsubscribe = subscribe(nextTheme => { <----- MAGIC
React-redux:
trySubscribe() {
if (shouldSubscribe && !this.unsubscribe) {
this.unsubscribe =
this.store.subscribe(this.handleChange); <----- MAGIC
}
}
componentDidMount() {
this.trySubscribe();
}
Thus, even if parent Pure Component will block any update enables you to catch a change, store update, context variable change, or everything else.
So — something inside pure components is very soiled and absolutely impure. It is driven by side effects!
But this bypass straight logic flow, and works just differently from the rest of application.
So — just be careful. And don’t forget about magic.
Aaaand….
And this is a reason, why any redux store update will cause redraw in each connected component, and why you should use reselect just next to connect HoC —
to stop unnecessary change propagation.
But you should read this from another point of view:
redux-connect is a source of a change propagation.
redux connect is the end of a change propagation. It is still PureComponent.
And this leads to quite handy thing — you can control change propagation with redux-connect only. Just create a boundaries for a change. Lets talk about this in another article.
Conclusion
Pure components keep your application fast. Sometimes — more predictable, but often — less predictable, as long they change the way application works.
Stateless components are not pure, and may run slower than PureComponents by any kind.
But… if you very wish to create a fast application with good user experience — you have to use Pure Component.
No choice. But, now — you know hidden truth, and knew some magic…
React recommends that ajax request be made in componentDidMount(), rather than in componentWillMount(). For more info on this, read this post.
Since you want to make ajax requests in componentDidMount(), you need a class. There are two ways of writing component definitions: functional component and the class component. Functional components are more concise, but you don't get component lifecycle methods like componentDidMount(). Think of it as just a render function that takes props as inputs and outputs DOMs (in JSX). To override those lifecycle methods, you need to define them as a class.
If you want to use Redux, and want to make ajax requests in a Redux action, you should import the action creator function (fetchUser(..) in your case) that makes the ajax request, and dispatch(fetchUser(..)) in componentDidMount(). connect(..)ed components get dispatch(..) function passed to it by Redux store.
If you want to see how it's done in other redux apps, see the official example apps in the redux.js repo, paying attention to actions and containers: https://github.com/reactjs/redux/tree/master/examples
In Your case you can continue with statefull components no wrong in that
,If you need to go with functional way
There is a work arround
https://github.com/mobxjs/mobx/issues/162
Suggestion
Calling the api in componentDidMount will make sense than
componentWillMount , Because you can show the user something is
fetching.
I think,User component is designed nicely.It will act as a container for Profile to provide the Data.
Instead of making Profile component class oriented,it should be Stateless.
Lets User component pass the required data for Profile component.
You don't need to connect Profile component using redux-connect.Just render it as a Child component of User.
Profile
const Profile = (props) => {
const {user, likeProfile} = props;
//likeProfile()//call like this using dom event or programmatically.
return (
<h1>{user.profile.name}</h1>
)
}
You need to make some changes in User component.
Get the state for Profile component via mapStateToProps.
class User extends React.Component {
componentWillMount() {
this.props.fetchUser(.....);
}
render() {
const {user, likeProfile} = this.props;
return (
<Profile user= {user} likeProfile={likeProfile} /> //passed the user data to Profile component vua User
)
}
Map the user state for Profile in User connect.
const mapStateToProps = (state)=>{
return{
user : state.somereducerkey.user //this will be accessible in Profile via props { user}
}
}
export default connect(mapStateToProps, {fetchUser, likeProfile})(User);

connect(null, null, null, { withRef: true }) unconventional?

I have a need to pull some content out of a child component such that the parent (or just generally higher-level) component can dispatch an action creator with the data of the inner child component.
Previously I was invoking a function on the child ref like this.refs.child.doStuff() until I found it was becoming difficult to keep track of the ref in whatever parent component I needed the method data in. Everytime there is a HOC or some other kind of compositing wrapper I need to add more code to pass the ref up the chain that I need. Not to mention there was a lot of duplicate code in each child function that was standard to all.
// child.js
class InnerComponent extends React.PureComponent {
doStuff = () => {
calculateFrom(this.state.content)
// and then do stuff with it..
// I've since made this more redux-y by just returning the data
// out to an action dispatcher
}
}
const SomeHOC = (args) => {
return (Component) => class extends React.Component {
proc(wrappedComponentInstance) {
// I went with using the getWrappedInstance func here
// to mimic conect(.., .., .. { withRef: true }) for
// basic compatibility with a higher level function that
// dives through the wrappedInstances to get to the bottom one
this.getWrappedInstance = () => wrappedComponentInstance;
}
render() {
const props = Object.assign({}, this.props, { ref: this.proc.bind(this) });
return <Component {...props} {...this.state} />
}
}
}
// I actually don't need the connect() here but will on other components of the same style when they're not in their wrapped form
export default connect(..., ..., null, { withRef: true })(SomeHOC()(InnerComponent);
// parent container (there are multiples of these in my app)
class Container extends React.PureComponent {
determineContent = (...) => {
return React.createElement(this.state.content, {
// so I can get to the composited inner element...
ref: (element) => { this._compositeElement = element; },
...viewletProps
});
}
componentWillMount() {
// ...
System.import(`${dynamic}.jsx`).then((content) => {
this.setState({ content });
});
}
renderButtonContainer = () => {
// Here's where things get weird...
if (!this.doStuff) {
this.doStuff = ((() => {
// hook into my ref
const compositeElement = this._compositeElement;
// deep-dive through to get to the base
let base;
if (compositeElement && typeof compositeElement.getWrappedInstance === 'function') {
base = this._compositeElement.getWrappedInstance();
while (typeof base.getWrappedInstance === 'function') {
base = base.getWrappedInstance();
}
if (typeof compositeElement.doStuff === 'function') {
this.doStuff = base.doStuff;
}
}
})());
}
return (
<div hidden={!this.doStuff}>
<span><i class="myIcon" onClick={this.doStuff}></i></span>
</div>
);
}
render() {
<div>
{ this.renderButtonContainer() }
<div>
{ this.determineContent(...) }
</div>
</div>
}
}
I've since pulled all of that out and am now dispatching an action per Redux-style and letting my reducer handle what needs to happen (just a synchronous internal call that executes immediately using the data in the action; I'm still unsure if this being in the reducer is bad form)
However, since I need my child components to still return the invocation of their doStuff() (getStuff() instead) at the time of the parent component's choosing, I find myself stuck with my same ref issues.
Am I going about this all wrong? It almost seems to me like for each child component I have I need to be storing this always-changing data from getStuff() inside my state model and just pass it down into the component? But I'd anticipate that would be too disassociating from the actual component and the rest of my app doesn't really care about that.
Frankly, none of that seems like a good idea at all. Even if it's technically possible, it completely goes against the intended usage of both React and Redux.
Refs in general are an escape hatch, and should be used only if necessary. Refs to DOM nodes are more useful, because you may need to do things like determining if a click is inside a DOM node, or read a value from an uncontrolled input. Refs to components have a lot fewer use cases. In particular, directly calling methods on components is definitely not idiomatic React usage, and should be avoided if at all possible. From there, groveling into the guts of React's implementation is a really bad idea.
If you need to make use of a nested child's data in a parent component, and those are widely separated, then you should either pass some kind of callback prop down through all those children, or you should dispatch a Redux action to put the data into the store and have the parent subscribe to that data.
So yeah, it's hard to tell exactly what you're actually needing to get done from that description and example code, but I can safely say that what you've got there is not the right way to do it.
#markerikson there is valid reasons for doing this. React unidirectional data flow is a very important fundamental rule, but just like how the best music breaks some rules (jazz improv), so too do the best React apps break some conventions in very crafty places. For example, Redux and React router must break convention by using context. So too is there valid use cases for using refs and accessing components with refs.
For example: Imagine you have a very complicated text editor component as a parent. You have many child components responsible for the fields such as a contact field a website field and a description field etc. Now, inside each of these child components you frequently update state. Specifically, on every single click of a button you update the state. The product ask is to have all of the fields alive at the same time so you cannot have individual submit buttons on the fields. You must take all of the current state of all fields at the same time. Now, you could manage the state by lifting it to the complicated parent component, but then that means every single time you press a button to re-render any of these fields you are going to have a callback that causes the complex parent to rerender and therefore all other children components.
On the other hand, with the crafty use of the nifty trick component refs we can update the state of all fields completely independently without triggering rerenders in everything, then when the user submits the form we can trigger one function inside the parent that checks the current/final state of all children through refs and submit that value to the database.
The idea of antipatterns comes up in React a lot. You are right to stick to the rules most of the time, but like a jazz pianist takes the standard Saints Go Marching In and breaks some rules to make something more enjoyable to some people, we can take React, a rigid set of conventions and improv on them if it creates a better experience for certain contexts.

Prevent react component from rendering twice when using redux with componentWillMount

I have a React component that dispatches a redux state change in its componentWillMount function. The reason is that when the component is loaded, it needs to get the id from the url (powered by react-router), and trigger an action that sets up the state with that id's data.
Here is the component:
class Editor extends React.Component {
componentWillMount() {
const { dispatch, params } = this.props
dispatch(editItem(params.id))
}
render() {
const item = this.props.item
console.log("Editing", item)
}
}
export default connect(state => ({item: state.item}))(Editor)
Here's the catch: render is getting called twice. item is undefined on the first call, and valid on the second. Ideally, it should only be called once this.props.item actually exists (after the editItem action has been dispatched and run).
According to the React docs: "If you call setState within this method, render() will see the updated state and will be executed only once despite the state change."
In redux, dispatch is the equivalent of calling setState, as it results in a state change. However, I'm guessing something in the way connect works is still causing render to be called twice.
Is there a way around this besides adding a line like if (!item) return; ?
One thing you might do is create a higher order component that handles the basic pattern of loading a different component (or no component) before the required props are loaded.
export const LoaderWrapper = function(hasLoaded, Component, LoaderComponent, onLoad) {
return props => {
if (hasLoaded(props)) {
return <Component {...props} />
}
else {
if (onLoad) onLoad(props)
return { LoaderComponent ? <LoaderComponent /> : null }
}
}
}
Then you can wrap your component before connecting it to get the desired behaviour.
export default connect(state => ({item: state.item}))(LoaderWrapper(
((props) => !!props.item),
Editor,
null,
(props) => props.dispatch(editItem(props.params.id))
))
You might want to add some currying magic to make sure you can compose these kinds of wrapper functions more nicely. Take a look at recompose for more info.
It looks like there's already an issue in the react-redux library.
https://github.com/rackt/react-redux/issues/210
What does editItem do? Does it add item to the redux state or is it there already?
If it is adding I imagine what is happening is that a render cycle happens with the current props, ie item being blank.
Then it gets rendered again when the props have changed, via setting the item.
One approach to fixing this sort of thing is to create a higher order component that wraps Editor and calls the dispatch action the rendering though is set either to a loading screen or and empty div until item is set. That way you can be assured that Editor will have an item.
But without knowing what editItem does it's sort of hard to know. Maybe you could paste the code for that?

Updating state in more than one component at a time

I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components

Resources