Dynamic React Redux Header component in large application - reactjs

I'm building a Header component which is used throughout my application. I'm using React and Redux (obvz) to hold a default state for the Header e.g. in the header reducer's default state argument:
state = {
showUserMenu: true,
redirectUrl: '/'
}
This is perfect for all the components where this is true, but for some routes/components I want the header to not show the user menu.
So when those components mount I dispatch an action to HIDE_USER_MENU.
The problem is that because the default is set to true, the userMenu will be there initially, and even if the dispatch is called in componentWillMount, there will be a split second where the userMenu shows.
So don't have a default? But then the reverse is true, it defaults to not showing the menu and only appears once the action has processed.
This is nice, but it didn't go just a step further (for my example) and explain how the reducer would be chosen based on either route or component.
I have also tried firing actions based on location.pathname using react-router-redux, but even this does not happen quickly enough to avoid FOUH (flash of unwanted header! :'( )
I wondered if there was an established pattern for dynamically loading an initial state, that is guaranteed to appear on initial render.
Hope it's clear what I was asking, any help much appreciated!

The simplest approach is to have the Header be a child of the route. This makes it easy to decide what props to pass:
* You don't need to store the header state at all - the routed component can just pass the appropriate props (such as showUserMenu) to the Header.
However, it has a couple of downsides:
* All routes have to take care of rendering the Header (not really a problem, there are lots of ways to share the code)
* When re-routing, the Header is unmounted and a new one mounted (since its parent, the route component, is unmounted). So any DOM and React state will be lost.
The unmount could be avoided by using the same component type for all your routes (which could render the header), but pass it props to configure the appropriate route handling behaviour (such as a child component).
Another (probably bad) option is for your Header state listen for redux-react-router's LOCATION_CHANGE action, and change the value of showUserMenu based on that. The documentation appears to advise against this, since there can be some asynchronicity before the new route is actually rendered due to dynamic route loading, etc., but it probably works fine for the normal case.

Related

Permanent States in React - React-Router

I have a problem: I have a NavBar that loads an AJAX which retrieves a name and another data. Well, the problem is that this data stores them in states of the declared component itself as NavBar.jsx and when I use routes (react-router) the component is mounted and dismantled losing its states, therefore, every time that change of route has That re-make the AJAX call. This same problem I have for a button 'Enter', which has status 1 and when someone is logged in takes 0 status, but every time I change the route returns to state 1 (as if it were not logged, although it is actually logged). My question is: How can I create states that are maintained despite changing routes? (I know this is not possible, with states I am referring to variables, or anything I can declare that does not change value every time you reassemble the components, in this case NavBar, when changing paths. Thank you.
This can be solved in several ways.
save the returned data in localStorage, check in localStorage before doing the ajax call, is the data exists, just use it instead.
Use redux - this moves the state to the app level instead of the component, unmounting the component will not delete the state.
do not re-render the navbar on each navigation(if it's possible in your app).
See the tutorial for nested routes for react-router ;
https://github.com/reactjs/react-router-tutorial/tree/master/lessons/04-nested-routes
this option means that route changes will only re-render the "internal page" and not constant things like the navbar, submenu, footer etc.

Editing a form is not working on react redux

I am new on react. I am working on react application with redux. I have a form (I am using redux-form) by which I can save data or edit data.
my problem is , In edit mode I populate data using componentWillReceiveProps. and populated perfectly, now when I try to clear any field on form its again fill.
componentWillReceiveProps(nextProps){
this.props.dispatch(initialize('NewProject', nextProps.project.project[0]));
}
I would be grateful for any help.
Is there a reason you're not dispatching this action somewhere else, like in componentDidMount? I can't say without seeing more code, but it's possible that whenever you edit your form, React again calls componentWillReceiveProps and overwrites whatever you did with the behavior you've given your component.
Per the React documentation:
Note that React may call this method even if the props have not changed, so make sure to compare the current and next values if you only want to handle changes. This may occur when the parent component causes your component to re-render.
It may be a good idea for you to move your dispatch to a more predictable event if possible, like componentDidMount. Typically, your render method should be capable of handling different cases if a component has multiple possible states. You could also create an edit and save version of your component, and render one or the other based on the props you receive. The best way to "populate" data, as you put it, is to define propTypes for your component, and then use your props to insert that data into elements in the render method.

Redux global solution for Cannot read property of undefined in mapStateToProps if store is not yet hydrated

After migrating to react redux land, I'm in love.. yet my heart breaks every time i try to render a route and gets a Cannot read property 'name' of undefined in the mapStateToProps function which is trying to extract a slice of state that is not loaded yet.
Example:
state = { posts:{ blog : {byId:{},ids:[]}, wall: {byId:{},ids{} } };
Say I do fetch all data from the server once the app starts up "index.js entry point". Based on the assumption that all data is loaded, I code multiple components to filter this data and work on it.
Trouble happens when a user opens the app and navigates too fast to a page where the data is not yet loaded. Then I get the hated error of "Cannot read property 'name' of undefined"
Example:
//route ::/posts/blog/1 has something like this
export default connect(
(state,prop)=>({
postId: state.posts.blog.byId[props.params.id].id
}))(props=><div>Post Id:{props.postId}</div>);
Now this components if loaded before blog store is populated it will give an error.
An option is to return a placeholder to connect and check first that post is loaded, or to use a magical proxy in initialState
initialState.blog = {byId:new Proxy({},{get:(obj,name)=>(name in obj)?obj[name]:{}}),ids[]}
//this will suppress error by returing an empty object if undefined byId
Is there any way i can REQUIRE a component to load its data by dispatching an action for example before it even renders?
What i want is something like what propTypes does except that it will defer loading the component or its connect function until the store confirms that it has finished loading.
Idea from propTypes:
Component.propTypes = {
dispatch:PropTypes.func.isRequired,
};
This will give an error if dispatch is not supplied as a prop for component
i want something similar but for store.
Example:
Component.requireData = (state)=>({
posts.blog:<placeHolder actionToDispatch="POST/BLOG/LOAD"/>, //if not posts.blog.isLoaded render placeholdercomponent insteed.
});
So it happens automagically if the required data is not loaded yet, the component will not render, and the placeholder will render in place dispatching an action that load the required missing data.
Whenever a component's render is called, it means the parent decided the component should exist. mapStateToProps is free to examine the state in any detail it needs to and produce any props from it as it sees fit. If a part of the state is missing, it can substitute some other props.
Some part of your system will have to decide when a place holder should be rendered rather than the data presentation component. The component that decides this may be the component itself or a parent. If it's a parent then you can make this component reusable.
No matter which way you turn this, you have to code the part that decides if all the data is ready. There's no magic. At the very least you'd need to pass a field name, but a function would be more flexible. At this point you may as well plug this function into the connect directly.
As such it seems to me that just writing a simple reusable parent component that takes a boolean prop that decides whether to show the child or a placeholder is not only a good and simple solution, but it seems to me that any other solution may not be substantially better or more "magical". Also "magic" is generally regarded as a bad idea these days because it implies a lack of transparency. By sticking to the React-Redux way, you are not adding hard to understand code.
Finally, don't expect either React or Redux to know what to do when your app doesn't provide the data that your app requires. Either don't require it or provide it. Creating a simple reusable parent component solves the issue by not requiring the data (and deciding what to do instead, which neither React, nor Redux, nor probably a third party library knows to decide).
Recap: create your own magic. It's simple, quick, and perfectly tuned to your needs.

Is there a clean way to conditionally load and render different components for the same React Router route?

The use case is that I want to map the root (/) to one of two different components based on whether the user is logged in or not, and I want these two components to reside in different bundles and lazily loaded, so simply putting the login check in the render() method would not do.
I tried to use dynamic route definition with require.ensure() to lazily load the component, and it works for the first time, but after changing the login state the component doesn't get updated (even if I navigate to another route and back to / ).
I tried to force re-rendering the router by setting props on the component that contains the router, both manually and by making it a Redux connected component, and I also tried to add a listener to the Redux store and change the component state in response to login change, but in all of the attempts I got the error "You cannot change ; it will be ignored" and the component doesn't change.
My ugly solution is to have the different component loading code outside of the router, listen to the login state change and in response load the matching component and set it in the wrapping component's state, which is referenced in the render() code. Is there a clean "React-Router-ish" way to do what I want?
React Router 4 pretty much solves this as it made the route configuration part of the component rendering, so having conditional rendering is the same whether it's based on the location or on other props/state.
The closest thing to a clean "React-Router-ish" way to do that is to use the React Router Enterhooks.
An enter hook is a user-defined function that is called when a route is about to be rendered. It receives the next router state as its first argument. The replace function may be used to trigger a transition to a different URL.
So, use the onEnter(nextState, replace, callback?) attribute on your <Route />.
Called when a route is about to be entered. It provides the next router state and a function to redirect to another path. this will be the route instance that triggered the hook.
If callback is listed as a 3rd argument, this hook will run asynchronously, and the transition will block until callback is called.
The general best practice I follow is to place the auth-check flow away from your routes, and place it inside the transition events/hooks.
The usual behavior is - before the route handler actually gets rendered, check the auth, and redirect the user to another route. In your case, if you want to use the same route, but render different components - you should be able to do that using the same technique too. However, that's not a common thing (based on what I've seen), but it should be possible.
For a complete example of this approach, here's the auth-flow code example you can check. It is shared by the creators of React Router, so it looks credible to me.
PS: My answer is valid for React Router versions > 0.13.x.

ReactJS: Why is passing the component initial state a prop an anti-pattern?

I've created a small ReactJS dashboard with the help of SocketIO for live updates. Even though I have the dashboard updating, it bugs me that I'm not quite sure if I did it correctly.
What bugs me the most is the Props in getInitialState as anti-pattern post. I've created a dashboard that gets live updates from a server, requiring no user interaction beyond loading the page. From what I've read, this.state should contain things that will determine whether the component should be re-rendered, and this.props.... I don't know yet.
However, when you initially call React.render(<MyComponent />, ...), you can only pass props. In my case, I get all data from the server, so the initial props just end up in this.state anyway. So all of my components have something like this:
getInitialState: function() {
return {
progress: this.props.progress,
latest_update: this.props.latest_update,
nearest_center: this.props.nearest_center
}
}
Which, unless I've misinterpreted the aforementioned blog post, is an anti-pattern. But I see no other way of injecting the state into the Component, and I don't understand why it's an anti-pattern unless I relabel all of my props to prepend initial on them. If anything, I feel like that's an anti-pattern because now I have to keep track of more variables than I did before (those prepended with initial and those without).
Disclaimer: When I answered this question I was learning / trying to
implement vanilla Flux and I was a bit skeptic about it. Later on I
migrated everything to Redux. So, an advice: Just go with Redux or
MobX. Chances are you won't even need the answer to this question
anymore (except for the science).
Passing the intial state to a component as a prop is an anti-pattern because the getInitialState method is only called the first time the component renders. Meaning that, if you re-render that component passing a different value as a prop, the component will not react accordingly, because the component will keep the state from the first time it was rendered. It's very error prone.
And here is what you should do:
Try to make your components as stateless as possible. Stateless components are easier to test because they render an output based on an input. Simple like that.
But hey.. my components data change.. I can't make them stateless
Yes you can, for most of them. In order to do that, select an outer component to be the state holder. Using your example, you could create a Dashboard component that contains the data, and a Widget component that is completely stateless. The Dashboard is responsible for getting all the data and then rendering multiple Widgets that receive everything they need through props.
But my widgets have some state.. the user can configure them. How do I make them stateless?
Your Widget can expose events that, when handled, cause the state contained in Dashboard to change, causing every Widget to be rerendered. You create "events" in your Widget by having props that receive a function.
Ok, so now, Dashboard keeps the state, but how do I pass the initial state to it?
You have two options. The most recomended one, is that you make an Ajax call in the Dashboard getInitialState method to get the initial state from the server. You can also use Flux, which is a more sophisticated way for managing data. Flux is more of a pattern, rather than an implementation. You can use pure Flux with the Facebook's implementation of the Dispatcher, but you can use third-party implementations like Redux, Alt or Fluxxor.
Alternatively, you can pass this initial state as a prop to the Dashboard, explicitly declaring that this is just the initial state.. like initialData, for instance. If you choose this path, though, you can't pass a different initial state to it aftwards, because it will "remember" the state after the first render.
OBS
You are not quite right in your definitions.
State is used to store mutable data, that is, data that is going to change during the component life-cycle. Changes in the state should be made through the setState method and will cause the component to re-render.
Props are used to pass in imutable data to the components. They should not change during the component life-cycle. Components that only use props are stateless.
This is a relevant source on the "how to pass the initial state to components".

Resources