Redux-react connect function does not work for root component - javascript

I am using a middleware to restore Redux store from indexeddb. I want to use that data in the root component for routing, to check if user is connected. But it seems that the connect function does not connect the root component state to Redux.
When I was debugging I saw mapStateToProps never actually runs. This is the relevant part of index.js:
class AppProvider extends Component {
constructor (props) {
super(props)
this.state = {}
}
componentWillMount () {
persistStore(store, {storage: localforage}) // Populates the store from indexedDB
}
render () {
return (
<Provider store={store}>
<Router history={history}> // Here I need the user from redux
<Route path="/login" render={() => (
this.state.user ? ( <Redirect to="/dashboard"/> ) : ( <LoginPage/> )
)}/>
</Router>
</Provider>
)
}
}
function mapStateToProps (state, ownProps) {
return {
user: state.user // function never runs!
}
}
withRouter(connect(mapStateToProps, {})(AppProvider))
render(
<AppProvider/>,
document.getElementById('root')
)
UPDATE:
When trying this:
const Root = withRouter(connect(mapStateToProps, {})(AppProvider))
render(
<Root/>,
document.getElementById('root')
)
I get
Uncaught TypeError: Cannot read property 'route' of undefined

You're not using the connected component.
Neither withRouter nor connect modify the original component. They simply return a new component.
Try this;
const AppProviderWithRedux = withRouter(connect(mapStateToProps, {})(AppProvider))
render(
<AppProviderWithRedux/>,
document.getElementById('root')
)

Related

The prop `push` is marked as required

I'm getting this warning when i call the ReactDOM.render() method:
Failed prop type: The prop push is marked as required in
Connect(Detail), but its value is undefined
This is the render call:
const render = Component => {
const appElement = document.getElementById('app');
ReactDOM.render(
<AppContainer>
{Component}
</AppContainer>,
appElement
);
};
And this is my component:
const component = (
<Provider store={store}>
<ConnectedRouter history={createBrowserHistory()}>
{routes()}
</ConnectedRouter>
</Provider>
);
Then, I call it like this:
render(component);
Do I need extra configuration on the ConnectedRouter or maybe I'm not building properly the routes and/or store?

How to get resultsState of react-instantsearch correctly with server-side render and integration with redux store

I try to get resultsState by call react-instantsearch's findResultsState API, But can't integrate with the component already connected with redux store. Error will show like below.
Invariant Violation: Could not find "store" in either the context or props of component . Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to component
Call api like this
findResultsState(
component: Search, {
searchState
});
My component looks like this. It is composed with some HOCs, and some HOC will connect with redux store through react-redux connect function.
Search = compose(
intlWrapper,
withModal,
withGlobalAlert,
withMainLayout,
injectIntl,
)(Search);
Server-side Provider will render like this
const appString = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={ context }>
<Main />
</StaticRouter>
</Provider>
);
Client-side:
const store = configureStore(history,
window.__APP_INITIAL_REDUX_STATE__);
// Render component
hydrate(
<Provider store={store}>
<ConnectedRouter history={history}>
<Main />
</ConnectedRouter>
</Provider>,
document.getElementById('root'),
);
Main
<Switch>
{ routes.map( route => <Route key={ route.path } { ...route } />)}
</Switch>
routes
export default [
{
path: '/search',
component: Search,
},{
...
}
...
];

ReactJS Conditional Header with Redux

I want to create a conditional component called HeaderControl that can generate a type of Header for the aplication if the user is Logged or not.
This is my Header.jsx :
import React from 'react';
import { connect } from 'react-redux';
import { isAuthenticated } from '../Login/reducers';
import ucaLogo from '../../assets/logo.gif';
import '../../styles/base.scss';
const mapStateToProps = state => ({
isAuthenticated: isAuthenticated(state),
});
function HeaderNoLogin() {
return <div className="header-div col-md-12">
<img className="img-login-header" src={ucaLogo} alt="logo" />
<div className="title-head-div">
<p className="title-head">Not logged</p>
<p className="title-head"> Not logged</p>
</div>
</div>;
}
function HeaderLogged() {
return <div className="header-div col-md-12">
<img className="img-login-header" src={ucaLogo} alt="" />
<div className="title-head-div">
<p className="title-head">Logged</p>
<p className="title-head"> Logged</p>
</div>
</div>;
}
class HeaderControl extends React.Component {
render() {
const isLoggedIn = (props) => {
if (this.props.isAuthenticated) {
return true;
}
return false;
};
let button = null;
if (isLoggedIn) {
button = <HeaderLogged />;
} else {
button = <HeaderNoLogin />;
}
return (
<div>
{button}
</div>
);
}
}
export default connect(mapStateToProps)(HeaderControl);
My entry point (app.jsx) in have a Provider with the store like this:
const history = createHistory();
const store = configureStore(history);
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<MuiThemeProvider>
<Switch>
<Route path="/login" component={Login} />
<PrivateRoute path="/" component={Home} />
</Switch>
</MuiThemeProvider>
</ConnectedRouter>
</Provider>,
document.getElementById('app'),
);
My questions are:
Where and how can I check if the user is authenticate using the redux store?
Where and how should I import the Header ? I think that I should import it in the app.jsx but I do not know where exactly.
Sorry if these are dumbs questions but this is the first time that I am using Reactjs with Redux.
Thanks.
If your a newbie docs has good example in react-router v4 https://reacttraining.com/react-router/web/guides/philosophy.
index.js:
export const store = createStore(
app,
composeWithDevTools(applyMiddleware(thunk))
); //mandatory to use async in redux
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<Router>
<App />
</Router>
</ConnectedRouter>
</Provider>,
document.getElementById("root")
);
And in app.js:
const App = () => (
<div>
<HeaderControl /> // header will be always visible
<MuiThemeProvider>
<Switch>
<Route path="/login" component={Login} />
<PrivateRoute path="/" component={Home} />
</Switch>
</MuiThemeProvider>
</div>
);
export default App;
Handle async logic with redux is not intuitive you shouldn't start by that if your new to redux.
reducers.js:
import {LOGGED_IN, LOGGED_OUT} from '../actions/User'
const initialState = {
logged: false
}
// reducer
function User (state = initialState, action) {
switch (action.type) {
case LOGGED_IN:
return Object.assign({}, state, {logged: true}, action.payload)
case LOGGED_OUT:
return initialState
default:
return state
}
}
export default User
You neet to setup a middlewear to handle async request in redux. It is the more simple approach. there is others options like redux saga and redux-observable. In this example i use redux-thunk.
I advise you to check redux docs on middlweare
https://redux.js.org/docs/advanced/Middleware.html
and/or this course on udemy https://www.udemy.com/react-redux-tutorial/
export const checkLogin = payload => {
return dispatch =>
xhr
.post(/login , payload) // payload is json name and password
.then(res => dispatch(LOGGED_IN))) // response is positive
.cath(err => console.log(err))
}
And your checkLogin action should be using in login.js form
login.js
import checkLogin from action.js
class Login extends React.Component {
render() {
return (
<form submit={checkLogin}>
{" "}
// this is pseudo code but i hope it's clear , you take data from your
form and you you call cheklogin.
</form>
);
}
}
So the logic is pretty simple:
In login you call the action checkLogin
A request will start to you server with login+pass
When the request end login reducer is call.
Reducer update state.
Header is refresh.

In react router v4 why does BrowserRouter work only inside the main render function?

For some reason I can't find a way to import the whole router as a component into the main render function that appends it to the body.
When I do this, all works fine:
render(
<Provider store={store}>
<Router history={history}>
<AppProvider store={store}/> // Containing the routing
</Router>
</Provider>,
document.getElementById('root')
)
But when I move everything into AppProvider like this
render(
<AppProvider store={store} history={history}/>,
document.getElementById('root')
)
AppProvider:
class AppProvider extends Component {
constructor (props) {
super(props)
this.state = {...}
}
render () {
return (
<Provider store={this.props.store}>
<Router history={this.props.history}>
/* ROUTES */
</Router>
</Provider>
)
}
}
export default withRouter(connect(mapStateToProps, {})(AppProvider))
I get
Uncaught TypeError: Cannot read property 'route' of undefined
Any ideas why?

Access redux store from redux-simple-router children

I'm trying to figure out how to access the redux store from within route so I can dispatch actions from within the route.
Here's what my top level Component looks like:
class App extends Component {
render() {
return (
<div>
{ children }
</div>
);
}
}
My redux-simple-router code looks like:
render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={ Home } />
<Route path="/example" component={ ExampleRoute } />
</Route>
</Router>
</Provider>,
rootElement
)
If I dump props from within the ExampleRoute component, I don't have access to the store. Any help appreciated!
You should use connect from react-redux to get dispatch and current state from the store. It is outlined in the redux docs here: http://rackt.org/redux/docs/basics/UsageWithReact.html
Here is your Example component:
//...
import { connect } from 'react-redux'
//...
export class Example extends Component {
render () {
const { dispatch, thingName } = this.props
return (
<button onClick={ () => {
dispatch(myAwesomeActionCreator())
}}>{ thingName }</button>
);
}
}
export default connect(state => state)(Example)
Some good examples of how to use connect can be found in the react-redux docs: https://github.com/rackt/react-redux/blob/master/docs/api.md#examples
I was able to get this working with "Monkeypatch" middleware, but there's got to be a better way.
First I created a function to monkeypatch the children variable. This function takes the child, the dispatch and the store as arguments, and returns an updated children variable with keys for the store and dispatch:
function routeStoreMiddleware (children, dispatch, store) {
return {
...children,
props: {
...children.props,
dispatch: dispatch,
store: store
}
}
}
Then I simply updated the component that already has access to the dispatch and store to consume the middleware function:
class App extends Component {
render() {
return (
<div>
{ routeStoreMiddleware(children, dispatch, store) }
</div>
);
}
}
Since the poorly named routeStoreMiddleware function simply returns an updated children object, it still works.
Now I can dispatch events and display data from within the ExampleRoute component.
import React, { Component } from 'react';
import { myAwesomeActionCreator } from '../actions.js'
export class Example extends Component {
render () {
const { dispatch, store } = this.props
return (
<button onClick={ () => {
dispatch(myAwesomeActionCreator())
}}>{ store.thingName }</button>
);
}
}
Yay!
Please note:
I've been reading a lot here about how to make middleware properly in redux, but I haven't had time yet to understand it fully. There's a better way than I've done here.

Categories

Resources