I built an app starting from a super easy example on medium.
To summarize, it appears the user comes into my app at the bottom part. I then pass the user down through props:
function App({user)){
return <AppContainer user={users} />
}
export default withFirebaseAuth({
providers,
firebaseAppAuth,
})(App);
Later in my app I want to update the user:
function AppContainer( { signOut, user } ){
const [user, setUser] = useState(user);
firebase.firestore().collection('users').doc(user.uid).get().then(function(doc) {
if (doc.exists) {
setUser(doc.data())
But it doesn't work. What's the Best way to do this?
Additionally it would be ideal if the two API calls could be combined somehow.
Last question is - I don't really understand how the export part of the code works - is it like context and i should be able to access the data coming in as props all the way down? Can I refactor it into context?
Usually for similar API calls useEffect hook is used, as documentation states:
If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.
First I would add the firebase calls into that hook then secondly I would change the user variable's name, you have one coming through props and similarly you are defining with useState.
I would consider using as the following:
function AppContainer( { signOut, user } ){
const [currentUser, setCurrentUser] = useState(user);
useEffect(() => {
firebase.firestore().collection('users').doc(currentUser.uid).get().then(function(doc) {
if (doc.exists) {
setUser(doc.data());
}
});
}, []);
At the end, you will have the returned user information from firebase in currentUser state object.
I hope that helps!
Our React application talks to an API via a library that heavily depends on promises.
We might for example pass an "User resource" around. When we render a User component, this component gets access to the users' properties by calling await User.get() in componentDidMount.
The implication is that many (if not most) of our components all need to have a ready state property, and most of them need to both deal with the case of a first render, and render after the user properties have been received.
This feels clunky. One idea we had was to create components specifically responsible for doing the loading of data, and a second set of "view" components only responsible for rendering after the loading is complete.
One issue here is that the "view" components are often also forms and need to communicate state changes back up the tree.
Not hard to do, but I am mainly curious if there are known good patterns for tackling this sort of thing, or prior art I can base this off. Alternatively, I will also take other suggestions on how to deal with this pattern.
I am using this component with redux
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Spinner } from "#blueprintjs/core";
export default ({ action, selector, component, errorComponent }) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(action());
}, [dispatch, action]);
const DispatchFetch = () => {
const { data, isRequesting, error } = useSelector(selector());
if (!isRequesting && data) {
const Comp = component;
return <Comp data={data}></Comp>;
} else if (error) {
if (errorComponent) {
const ErrorComp = errorComponent;
return <ErrorComp error={error}></ErrorComp>;
}
return <div>{error}</div>;
}
return <Spinner></Spinner>;
};
return <DispatchFetch></DispatchFetch>;
};
I'm trying to understand the connect method of react-redux, and the functions it takes as parameters. In particular mapStateToProps().
The way I understand it, the return value of mapStateToProps will be an object derived from state (as it lives in the store), whose keys will be passed to your target component (the component connect is applied to) as props.
This means that the state as consumed by your target component can have a wildly different structure from the state as it is stored on your store.
Q: Is this OK?
Q: Is this expected?
Q: Is this an anti-pattern?
Yes, it is correct. Its just a helper function to have a simpler way to access your state properties
Imagine you have a posts key in your App state.posts
state.posts //
/*
{
currentPostId: "",
isFetching: false,
allPosts: {}
}
*/
And component Posts
By default connect()(Posts) will make all state props available for the connected Component
const Posts = ({posts}) => (
<div>
{/* access posts.isFetching, access posts.allPosts */}
</div>
)
Now when you map the state.posts to your component it gets a bit nicer
const Posts = ({isFetching, allPosts}) => (
<div>
{/* access isFetching, allPosts directly */}
</div>
)
connect(
state => state.posts
)(Posts)
mapDispatchToProps
normally you have to write dispatch(anActionCreator())
with bindActionCreators you can do it also more easily like
connect(
state => state.posts,
dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)
Now you can use it in your Component
const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
<div>
<button onClick={() => fetchPosts()} />Fetch posts</button>
{/* access isFetching, allPosts directly */}
</div>
)
Update on actionCreators..
An example of an actionCreator: deletePost
const deletePostAction = (id) => ({
action: 'DELETE_POST',
payload: { id },
})
So, bindActionCreators will just take your actions, wrap them into dispatch call. (I didn't read the source code of redux, but the implementation might look something like this:
const bindActionCreators = (actions, dispatch) => {
return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
return actionsMap;
}, {})
}
Q: Is this ok?
A: yes
Q: Is this expected?
Yes, this is expected (if you are using react-redux).
Q: Is this an anti-pattern?
A: No, this is not an anti-pattern.
It's called "connecting" your component or "making it smart". It's by design.
It allows you to decouple your component from your state an additional time which increases the modularity of your code. It also allows you to simplify your component state as a subset of your application state which, in fact, helps you comply with the Redux pattern.
Think about it this way: a store is supposed to contain the entire state of your application.
For large applications, this could contain dozens of properties nested many layers deep.
You don't want to haul all that around on each call (expensive).
Without mapStateToProps or some analog thereof, you would be tempted to carve up your state another way to improve performance/simplify.
You got the first part right:
Yes mapStateToProps has the Store state as an argument/param (provided by react-redux::connect) and its used to link the component with certain part of the store state.
By linking I mean the object returned by mapStateToProps will be provided at construction time as props and any subsequent change will be available through componentWillReceiveProps.
If you know the Observer design pattern it's exactly that or small variation of it.
An example would help make things clearer:
import React, {
Component,
} from 'react-native';
class ItemsContainer extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items, //provided by connect#mapStateToProps
filteredItems: this.filterItems(props.items, props.filters),
};
}
componentWillReceiveProps(nextProps) {
this.setState({
filteredItems: this.filterItems(this.state.items, nextProps.filters),
});
}
filterItems = (items, filters) => { /* return filtered list */ }
render() {
return (
<View>
// display the filtered items
</View>
);
}
}
module.exports = connect(
//mapStateToProps,
(state) => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
//the State.App & state.App.Items.List/Filters are reducers used as an example.
})
// mapDispatchToProps, that's another subject
)(ItemsContainer);
There can be another react component called itemsFilters that handle the display and persisting the filter state into Redux Store state, the Demo component is "listening" or "subscribed" to Redux Store state filters so whenever filters store state changes (with the help of filtersComponent) react-redux detect that there was a change and notify or "publish" all the listening/subscribed components by sending the changes to their componentWillReceiveProps which in this example will trigger a refilter of the items and refresh the display due to the fact that react state has changed.
Let me know if the example is confusing or not clear enough to provide a better explanation.
As for: This means that the state as consumed by your target component can have a wildly different structure from the state as it is stored on your store.
I didn't get the question, but just know that the react state (this.setState) is totally different from the Redux Store state!
The react state is used to handle the redraw and behavior of the react component. The react state is contained to the component exclusively.
The Redux Store state is a combination of Redux reducers states, each is responsible of managing a small portion app logic. Those reducers attributes can be accessed with the help of react-redux::connect#mapStateToProps by any component! Which make the Redux store state accessible app wide while component state is exclusive to itself.
This react & redux example is based off Mohamed Mellouki's example.
But validates using prettify and linting rules. Note that we define our props
and dispatch methods using PropTypes so that our compiler doesn't scream at us.
This example also included some lines of code that had been missing in Mohamed's
example. To use connect you will need to import it from react-redux. This
example also binds the method filterItems this will prevent scope problems in
the component. This source code has been auto formatted using JavaScript Prettify.
import React, { Component } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
class ItemsContainer extends Component {
constructor(props) {
super(props);
const { items, filters } = props;
this.state = {
items,
filteredItems: filterItems(items, filters),
};
this.filterItems = this.filterItems.bind(this);
}
componentWillReceiveProps(nextProps) {
const { itmes } = this.state;
const { filters } = nextProps;
this.setState({ filteredItems: filterItems(items, filters) });
}
filterItems = (items, filters) => {
/* return filtered list */
};
render() {
return <View>/*display the filtered items */</View>;
}
}
/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
items: PropTypes.array.isRequired,
filters: PropTypes.array.isRequired,
onMyAction: PropTypes.func.isRequired,
};
/*
map state to props
*/
const mapStateToProps = state => ({
items: state.App.Items.List,
filters: state.App.Items.Filters,
});
/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
onMyAction: value => {
dispatch(() => console.log(`${value}`));
},
});
/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);
This example code is a good template for a starting place for your component.
React-Redux connect is used to update store for every actions.
import { connect } from 'react-redux';
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default AppContainer;
It's very simply and clearly explained in this blog.
You can clone github project or copy paste the code from that blog to understand the Redux connect.
It's a simple concept. Redux creates a ubiquitous state object (a store) from the actions in the reducers. Like a React component, this state doesn't have to be explicitly coded anywhere, but it helps developers to see a default state object in the reducer file to visualise what is happening. You import the reducer in the component to access the file. Then mapStateToProps selects only the key/value pairs in the store that its component needs. Think of it like Redux creating a global version of a React component's
this.state = ({
cats = [],
dogs = []
})
It is impossible to change the structure of the state by using mapStateToProps(). What you are doing is choosing only the store's key/value pairs that the component needs and passing in the values (from a list of key/values in the store) to the props (local keys) in your component. You do this one value at a time in a list. No structure changes can occur in the process.
P.S. The store is local state. Reducers usually also pass state along to the database with Action Creators getting into the mix, but understand this simple concept first for this specific posting.
P.P.S. It is good practice to separate the reducers into separate files for each one and only import the reducer that the component needs.
Here's an outline/boilerplate for describing the behavior of mapStateToProps:
(This is a vastly simplified implementation of what a Redux container does.)
class MyComponentContainer extends Component {
mapStateToProps(state) {
// this function is specific to this particular container
return state.foo.bar;
}
render() {
// This is how you get the current state from Redux,
// and would be identical, no mater what mapStateToProps does
const { state } = this.context.store.getState();
const props = this.mapStateToProps(state);
return <MyComponent {...this.props} {...props} />;
}
}
and next
function buildReduxContainer(ChildComponentClass, mapStateToProps) {
return class Container extends Component {
render() {
const { state } = this.context.store.getState();
const props = mapStateToProps(state);
return <ChildComponentClass {...this.props} {...props} />;
}
}
}
Yes, you can do this. You can also even process the state and return the object.
function mapStateToProps(state){
let completed = someFunction (state);
return {
completed : completed,
}
}
This would be useful if you want to shift the logic related to state from render function to outside of it.
I would like to re-structure the statement that you mentioned which is:
This means that the state as consumed by your target component can
have a wildly different structure from the state as it is stored on
your store
You can say that the state consumed by your target component has a small portion of the state that is stored on the redux store. In other words, the state consumed by your component would be the sub-set of the state of the redux store.
As far as understanding the connect() method is concerned, it's fairly simple! connect() method has the power to add new props to your component and even override existing props. It is through this connect method that we can access the state of the redux store as well which is thrown to us by the Provider. A combination of which works in your favor and you get to add the state of your redux store to the props of your component.
Above is some theory and I would suggest you look at this video once to understand the syntax better.
import React from 'react';
import {connect} from 'react-redux';
import Userlist from './Userlist';
class Userdetails extends React.Component{
render(){
return(
<div>
<p>Name : <span>{this.props.user.name}</span></p>
<p>ID : <span>{this.props.user.id}</span></p>
<p>Working : <span>{this.props.user.Working}</span></p>
<p>Age : <span>{this.props.user.age}</span></p>
</div>
);
}
}
function mapStateToProps(state){
return {
user:state.activeUser
}
}
export default connect(mapStateToProps, null)(Userdetails);
I've developed a smallish standalone web app with React and Redux which is hosted on its own web server. We now want to reuse/integrate most parts of this app into another React/Redux web app.
In theory this should work quite nicely because all my React components, reducers and most action creators are pure. But I have a few action creators which return thunks that depend on the app state. They may dispatch async or sync actions, but that's not the issue here.
Let's say my root reducer looks like this:
const myAppReducer = combineReducers({
foo: fooReducer,
bar: barReducer,
baz: bazReducer
});
and my most complex action creators depend on many state slices (luckily there are only a few of those):
const someAction = function () {
return (dispatch, getState) => {
const state = getState();
if (state.foo.someProp && !state.bar.anotherProp) {
dispatch(fetchSomething(state.baz.currentId);
} else {
dispatch(doSomethingSynchronous());
}
};
}
Now the problem is that my action creators expect everything to be inside the root of the state object. But if we want to integrate this app into another redux app we'll have to mount my appReducer with its own key:
// The otherAppReducer that wants to integrate my appReducer
const otherAppReducer = combineReducers({
....
myApp: myAppReducer
});
This obviously breaks my action creators that return thunks and need to read app state, because now everything is contained in the "myApp" state slice.
I did a lot of research and thinking how to properly solve this the last few days, but it seems I'm the first one trying to integrate a Redux based app into another Redux based app.
A few hacks/ideas that came to mind so far:
Create my own thunk type so I can do instanceof checks in a custom thunk middleware and make it pass my thunks a custom getState function which will then return the correct state slice.
Mount my root reducer with it's own key and make my thunks depend on that key.
So far I think the best approach would be to create my own custom middleware, but I'm not really happy with the fact that other apps will now depend on my middleware and custom thunk type. I think there must be a more generic approach.
Any ideas/suggestions? How would you solve this kind of problem?
Have you considered not depending on store.getState()? I would decouple the actions from the application state altogether and take in the data you need from where the actions are called.
So for example:
const someAction = function (someProp, anotherProp, currentId) {
return dispatch => {
if (someProp && !anotherProp) {
dispatch(fetchSomething(currentId);
} else {
dispatch(doSomethingSynchronous());
}
};
}
This makes the actions totally reusable, with the downside of you having to now have that information elsewhere. Where else? If convenient, inside your component using this.context.store, or via props with connect, or maybe better, by having wrapper actions for your specific applications, so:
const someApplicationAction = () => {
return (dispatch, getState) => {
const { foo, bar, baz } = getState();
dispatch(someGenericAction(foo.someProp, bar.anotherProp, baz.currentID));
};
}
Update
Thanks to #Dominic Tobias and #gabdallah for spotting my embarrassing mistake.
The correct answer is of course;
so try checking action.payload.
The other comments regarding the switch statement and the action object we're referring to errors I made in my example, which I've since corrected.
Imagine I've combined the the following two reducers;
import { combineReducers } from 'redux'
import { routerStateReducer } from 'redux-router'
import entries from './entries'
export default combineReducers({
router: routerStateReducer,
entries
})
I would like to mutate the entries state based on another part of the global state, in this case; the router state provided by redux-router in order for example to implement pagination.
How could I do something like this?
// entries.js
import { ROUTER_DID_CHANGE } from 'redux-router/lib/constants'
const initialState = {}
function entries (state = initialState, action) {
switch (action.type) {
case ROUTER_DID_CHANGE:
// How can I access `state.router` here in order to do something like this;
if (routerState.location.pathname === '/entries') {
return {
...state,
page: routerState.location.query.page || state.page,
limit: routerState.location.query.limit || state.limit
}
}
return state
}
}
Some other approaches that come to mind;
connect the router state to the Entries route component, use the componentWillMount lifecycle method to check router state and call an action creator with the page and limit values mutating the entries state in turn. This would work; however I'm using some transition middleware to call a static fetchData method on the route component prior to mounting it, so the data get's fetched, then the pagination action creator would be called afterwards; not the desired behaviour.
listen to router actions somewhere else (i.e a dedicated router redux module), call an action creator on the entries store, but I'm not sure how well this fits with redux-router or how I would get access to the router part of the global store.
don't do this at all; simply query the router state in the static fetchData method
Other useful info;
The implementation in question is Universal App heavily inspired by react-redux-universal-hot-example
Relevant deps
react 0.14.2
redux 3.0.3
react-router 1.0.0-rc3
redux-router 1.0.0-beta3
How can I achieve this or similar behaviour? Am I even thinking about this the right way?
Back in the old days before things got simpler for a developer people would listen to the popstate event on the history object ;)
It looks like the required info is on the action?
history.listen((error, nextRouterState) => {
...
store.dispatch(routerDidChange(nextRouterState));
and the action:
export function routerDidChange(state) {
return {
type: ROUTER_DID_CHANGE,
payload: state
};
}
So try checking action.payload.
However your switch statement is using action instead of action.type so there's something fishy going on there.. You shouldn't need to do action = {} either - see http://redux.js.org/docs/basics/Reducers.html