React Lazy loading import failing - javascript

My component looks something like this, of course with a few unimportant details omitted:
import React from 'react';
import { createMuiTheme, MuiThemeProvider, withStyles } from "#material-ui/core/styles";
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
import Tooltip from '#material-ui/core/Tooltip';
import { connect } from 'react-redux';
const DialogBox = React.lazy(() => import('./DialogBox'));
const mapStateToProps = (state, ownProps) => {
return {
answer: state.answers[state.stepper][ownProps.obj.ID]
}
}
const mapDispatchToProps = { }
class FlexiblePopupSelect extends React.Component {
render() {
return (
<React.Fragment>
<DialogBox />
</React.Fragment>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(FlexiblePopupSelect));
When I replace the const DialogBox = React.lazy(() => import('./DialogBox')); line with a normal import DialogBox from './DialogBox', everything works fine. I followed this guide from React's site, but with no success. Where did I go wrong here?
EDIT:
There was no real error message, it just gives me a bunch of error messages that say "The above error occurred in one of your React components" but it never gives me any error message above.
I am using React 16.8.6 with Create-React-App handling the Webpack side of things.
EDIT 2:
After a bit of fiddling, I found out that the fix was using the <Suspense> component from react like so:
<React.Fragment>
<Suspense>
<DialogBox />
</Suspense>
</React.Fragment>

You need to wrap your lazily component with React.Suspense by providing the fallback component to show. (such as a message or a loading gif etc).
You can safely replace React.Fragment with React.Suspense.
class FlexiblePopupSelect extends React.Component {
render() {
return (
<React.Suspense fallback={<div>Loading dialog box...</div>}>
<DialogBox />
</React.Suspense>
)
}
}
For more info, check out Code-Splitting > Suspense documentation.

Related

How to use react-router-dom v6 navigate in class component

I installed react-router-dom v6 and I want to use a class based component, in previous version of react-router-dom v5 this.props.history() worked for redirect page after doing something but this code not working for v6 .
In react-router-dom v6 there is a hook useNavigate for functional component but I need to use it in class base component , Please help me how to use navigate in class component ?
In the react-router-dom v6, the support for history has been deprecated but instead of it, navigate has been introduced. If you want to redirect user to a specific page on success of a specific event, then follow the steps given below:
Create a file named as withRouter.js, and paste the code given below in this file:
import { useNavigate } from 'react-router-dom';
export const withRouter = (Component) => {
const Wrapper = (props) => {
const navigate = useNavigate();
return (
<Component
navigate={navigate}
{...props}
/>
);
};
return Wrapper;
};
Now, in whichever class based component you want to redirect the user to a specific path/component, import the above withRouter.js file there and use this.props.navigate('/your_path_here') function for the redirection.
For your help, a sample code showing the same has been given below:
import React from 'react';
import {withRouter} from '.your_Path_To_Withrouter_Here/withRouter';
class Your_Component_Name_Here extends React.Component{
constructor(){
super()
this.yourFunctionHere=this.yourFunctionHere.bind(this);
}
yourFunctionHere()
{
this.props.navigate('/your_path_here')
}
render()
{
return(
<div>
Your Component Code Here
</div>
)
}
}
export default withRouter(Your_Component_Name_Here);
Above Code works Perfect. And this is just a small extension.
If you want onclick function here is the code:
<div className = "row">
<button className= "btn btn-primary"
onClick={this.yourFunctionHere}>RedirectTo</button>
</div>
in class base component for redirect user follow this step :
first import some component like this
import { Navigate } from "react-router-dom"
now make a state for Return a boolean value like this:
state = {
redirect:false
}
now insert Naviagate component to bottom of your component tree
but use && for conditional rendring like this :
{
this.state.redirect && <Navigate to='/some_route' replace={true}/>
}
now when you want redirect user to some page just make true redirect state
on a line of code you want
now you can see you navigate to some page :)
Try this:
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
export const withRouter = (Component) => {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
and just used this function, in my case:
import { withRouter } from '../utils/with-router';
import './menu-item.styles.scss';
const MenuItem = ({title, imageUrl, size, linkUrl,router}) =>(
<div
className={`${size} menu-item`} onClick={() => router.navigate(`${router.location.pathname}${linkUrl}`)}
>
<div className='background-image'
style={{
backgroundImage: `url(${imageUrl})`
}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
)
export default withRouter(MenuItem);
I found this solution here https://www.reactfix.com/2022/02/fixed-how-can-i-use-withrouter-in-react.html
Other solution is useNavigate, for example:
<button onClick={() => {navigate("/dashboard");}} >
Dashboard
</button>
In a react class component use <Navigate>. From the react router docs:
A <Navigate> element changes the current location when it is rendered. It's a component wrapper around useNavigate, and accepts all the same arguments as props.
Try creating a reusable functional Component like a simple button and you can use it in your class component.
import React from "react";
import { useNavigate } from "react-router-dom";
const NavigateButton = ( { buttonTitle, route,isReplaced}) => {
const navigate = useNavigate();
return (
<button
className = "btn btn-primary"
onClick = { () => {
navigate( route , {replace:isReplaced} )
}}
>
{buttonTitle}
</button>;
);
});
export default NavigateButton;
After this, you can use NavigateButton in any of your class Components. And it will work.
<NavigateButton title = {"Route To"} route = {"/your_route/"} isReplaced = {false}/>
Found this explanation from the GitHub react-router issue thread, this explained how to use react-router 6 with class components
https://github.com/remix-run/react-router/issues/8146
I got this code from the above issue explanation
import React,{ Component} from "react";
import { useNavigate } from "react-router-dom";
export const withNavigation = (Component : Component) => {
return props => <Component {...props} navigate={useNavigate()} />;
}
//classComponent
class LoginPage extends React.Component{
submitHandler =(e) =>{
//successful login
this.props.navigate('/dashboard');
}
}
export default withNavigation(LoginPage);
If you need to use params for data fetching, writing a logic in your ClassComponent and render component depending on them, then create wrapper for your ClassComponentContainer
import { useLocation, useParams } from 'react-router-dom';
import ClassComponentContainer from './ClassComponentContainer';
export default function ClassComponentWrap(props) {
const location = useLocation();
const params = useParams();
return <ClassComponentContainer location={location} params={params} />
}
after it just use params in ClassComponent which is in props
import React from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import PresentationComponent from './PresentationComponent';
class ClassComponent extends React.Component {
componentDidMount() {
let postID = this.props.params.postID;
axios.get(`https://jsonplaceholder.typicode.com/posts/${postID}`)
.then((response) => {console.log(response)})
}
render() {
return <PresentationComponent {...this.props} />
}
}
const mapStateToProps = (state) => {...}
const mapDispatchToProps = (dispatch) => {...}
const ClassComponentContainer = connect(mapStateToProps, mapDispatchToProps)(ClassComponent);
export default ClassComponentContainer;
and use ClassComponentWrap component in Route element attribute
import { BrowserRouter, Route, Routes } from "react-router-dom";
import ClassComponentWrap from './components/ClassComponentWrap';
export default function App(props) {
return (
<BrowserRouter>
<Routes>
<Route path="/posts/:postID?" element={<ClassComponentWrap />} />
</Routes>
</BrowserRouter>
);
}
Here is my solution:
import React, { Component } from "react";
import { useNavigate } from "react-router-dom";
class OrdersView extends Component {
Test(props){
const navigate = useNavigate();
return(<div onClick={()=>{navigate('/')}}>test{props.test}</div>);
}
render() {
return (<div className="">
<this.Test test={'click me'}></this.Test>
</div>);
}
}

React Router & Global Context

I'm building an e-commerce website with React (my first ever React project) and I'm using React router to manage my pages.
I've got the following component tree structure:
<Router>
<BrowserRouter>
<Router>
<withRouter(Base)>
<Route>
<Base>
<BaseProvider>
<Context.Provider>
<Header>
<PageContent>
The standard React Router structure basically, and withRouter I've got the following:
Base.js
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { Header } from './Header';
import { Footer } from './Footer';
import Provider from '../../BaseProvider';
class Base extends Component {
render() {
return (
<Provider>
<Header/>
<div className="container">{this.props.children}</div>
<Footer />
</Provider>
);
}
}
BaseProvider.js
import React, { Component, createContext } from 'react';
const Context = createContext();
const { Provider, Consumer } = Context;
class BaseProvider extends Component {
state = {
cart: [],
basketTotal: 0,
priceTotal: 0,
};
addProductToCart = product => {
const cart = { ...this.state.cart };
cart[product.id] = product;
this.setState({ cart, basketTotal: Object.keys(cart).length });
};
render() {
return (
<Provider
value={{ state: this.state, addProductToCart: this.addProductToCart }}
>
{this.props.children}
</Provider>
);
}
}
export { Consumer };
export default BaseProvider;
This gives me a template essentially, so I just the children pages without having to include Header and Footer each time.
If I want to use my global context I'm having to import it each time, and it seems like I've done something wrong as surely I should be able to use this on any page since it's exported in BaseProvider?
If I was to visit the About page, I'd get the same component structure, but no access to the consumer without using:
import { Consumer } from '../../BaseProvider';
Why do I have to do this for each file even though it's exported and at the top level of my BaseProvider? It just seems such a bad pattern that I'd have to import it into about 20 files...
Without importing it, I just get:
Line 67: 'Consumer' is not defined no-undef
I tried just adding the contextType to base but I get: Warning: withRouter(Base): Function components do not support contextType.
Base.contextType = Consumer;
I feel like I've just implemented this wrong as surely this pattern should work a lot better.
I'd recommend using a Higher Order Component - a component that wraps other components with additional state or functionality.
const CartConsumer = Component => {
return class extends React.Component {
render() {
return (
<MyContext.Consumer>
<Component />
</MyContext.Consumer>
)
}
}
}
Then in any component where you'd like to use it, simply wrap in the export statement:
export default CartConsumer(ComponentWithContext)
This does not avoid importing completely, but it's far more minimal than using the consumer directly.

How to properly work with Router using history to pass variables?

I'm designing a website that just like facebook or anyother website that I seen I assume works (disclosure: I'm new to web programing), what I want to do is to route to different routes but in one of my routes or possibly even more I need to pass info to the next screen route when I to the new page for example: (I'm in
www.website.com/page1 then move to www.website.com/page1/page2) whilst passing data through the state say I want to pass a date or a name but I would not want it to be shown in url. So I found that react can pass with:
<Link {to={pathname:"/page2", state:{statetopass:datatopass}}}>
However,when I do pass the state once I'm in (www.website.com/page1/page2) I'm unable to read the data only when I refresh which i find weird will I ever see the data passed, I read that history is mutable but I can't really understand what that means its probably something to do with what my problem is.
The code that I have tried so far is here:
<-------------------- APP--------------------------->
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch, IndexRoute, HashRouter } from 'react-router-dom';
import Page1 from './Page1'
import Page2 from './Page2'
import { createBrowserHistory } from "history";
const history = createBrowserHistory();//idk if history should be here seems
class App extends Component {//to make no difference
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/hom/Page1' component={({ match }) => { return (<Page1 />) }} />
<Route exact path='/hom/Page1/Page2' component={({ match }) => { return (<Page2 />) }} />
</Switch>
</Router>
)
}
}
export default App;
<--------------------Page1----------------->
import React, { Component } from 'react';
import { Link } from 'react-router-dom'
class Page1 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<div>
<Link to={{
pathname: `Page1/Page2`,
state: { dataneed: "testme" }
}}><button>Check </button>
</Link>
</div>
)
}
}
export default Page1;
<-------------------------------Page2----------------------------->
import React, { Component } from 'react';
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
class Page2 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
console.log(history.location.state.dataneed)
return (
<div>
<h1>{history.location.state.dataneed}</h1>
</div>
)
}
}
export default Page2;
So you will see that at first you get an error but then once you refresh you see the text being displayed. If anyone could suggest the best way to go about doing whta I'm trying and if anyone could help me shed some light on the matter I would greatly appreciate this.
PS: I'm using 4.3.1 version there are videos out there but those seem to use version lower than 4.0 and completely different.
I believe the issue here is the mix of React Router and the History package. React Router uses History and has a history built in to its routers, so there is no need to explicitly use createBrowserHistory or anything from History directly. Specifically the issue is that the state is passed to the Link, from React Router, but then you attempt to access the data from the createBrowserHistory() object.
What you can do to resolve this issue and keep your code a bit cleaner is basically not use createBrowserHistory directly and instead rely on the built-in history from React Router. The routing data can be accessed through props.location, props.history, and props.match, which are injected into any component wrapped in the higher-order component withRouter from React Router.
What this will look like:
In index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
ReactDOM.render((
<BrowserRouter>
<App/>
</BrowserRouter>
), document.getElementById('root'));
In App.js:
import React, { Component } from 'react';
import { Route, Switch, withRouter } from 'react-router-dom';
import Page1 from './Page1'
import Page2 from './Page2'
class App extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
{/* Note no need for a Router because we wrapped the App component in the BrowserRouter in index.js */}
<Switch>
{/* Note the component attribute can be simplified */}
<Route exact path='/hom/Page1' component={ Page1 } />
<Route exact path='/hom/Page1/Page2' component={ Page2 } />
</Switch>
)
}
}
export default withRouter(App);
Page1 is fine.
In Page2:
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class Page2 extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
const { dataneed } = this.props.location.state;
return (
<div>
<h1>{ dataneed }</h1>
</div>
)
}
}
export default withRouter(Page2);
Hopefully this helps, let me know if you have questions!

react-router (v4) how to go back?

Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}

Redux: Single container, multiple components

I'm quite new to both React and Redux, and I'm unsure about both best practices and technical solution to a case I'm working on. I'm using "component" and "container" as defined by Dan Abramov here.
The component I'm working on is a small collection of filter components: One input text field and two buttons, all filtering a list of entities. I've tried two approaches:
First approach: Single component containing three instances of two types of containers, containers connected to corresponding components.
This was what I first made. Here, the root component looks like the following:
import React, { PropTypes, Component } from 'react';
import Config from '../../config';
import FilterInput from '../containers/FilterInput';
import FilterLink from '../containers/FilterLink'
class FilterController extends Component {
render() {
return (
<div className='filterController'>
<FilterInput displayName="Search" filterName={Config.filters.WITH_TEXT} />
<FilterLink displayName="Today" filterName={Config.filters.IS_TODAY} />
<FilterLink displayName="On TV" filterName={Config.filters.ON_TV} />
</div>
)
}
}
export default FilterController;
The two containers referenced here look pretty much as expected, as do the connected components. I'll show the FilterLink as an example:
import React, { PropTypes, Component } from 'react';
import {connect} from 'react-redux';
import {toggleFilter} from '../actions';
import FilterButton from '../components/filterbutton'
const mapStateToProps = (state, ownProps) => {
return {
active: !!state.program.filters[ownProps.filterName]
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(toggleFilter(ownProps.filterName, ownProps.input))
}
}
}
const FilterLink = connect(
mapStateToProps,
mapDispatchToProps
)(FilterButton)
export default FilterLink
And the corresponding FilterButton component:
import React, { PropTypes, Component } from 'react';
class FilterButton extends Component {
render() {
return (
<button className={this.props.active ? 'active' : ''}
onClick={this.props.onClick}>
{this.props.displayName}
</button>
)
}
}
FilterButton.propTypes = {
active: PropTypes.bool.isRequired,
displayName: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
filterName: PropTypes.string.isRequired
};
export default FilterButton;
This approach works, but I'm thinking that it shouldn't be necessary to create two different containers. Which again lead me to my second attempt.
Second approach: single container containing multiple components.
Here, I made a larger container:
import React, { PropTypes, Component } from 'react';
import Config from '../../config';
import {connect} from 'react-redux';
import {toggleFilter} from '../actions';
import FilterButton from '../components/filterbutton'
import FilterInput from '../components/filterinput'
class FilterContainer extends Component {
render() {
const { active, currentInput, onChange, onClick } = this.props;
return (
<div className='filterController'>
<FilterInput displayName="Search" filterName={Config.filters.WITH_TEXT} currentInput={currentInput} onChange={onChange} />
<FilterButton displayName="Today" filterName={Config.filters.IS_TODAY} active={active} onClick={onClick}/>
<FilterButton displayName="On TV" filterName={Config.filters.ON_TV} active={active} onClick={onClick}/>
</div>
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
active: !!state.program.filters[ownProps.filterName],
currentInput: state.program.filters[ownProps.filterName] ? state.program.filters[ownProps.filterName].input : ''
}
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(toggleFilter(ownProps.filterName, ownProps.input))
},
onChange: newValue => {
dispatch(toggleFilter(ownProps.filterName, newValue.target.value))
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(FilterContainer);
Here, all state interaction for the entire component is gathered in a single compoment. The components are the same here as in the first approach. This doesn't, however, really work: ownProps is empty in both mapStateToProps and mapDispathToProps. I may have misunderstood how the react-redux connection works.
So, given these things I have two questions: What's the best way to structure this component, in terms of containers and components? And secondly, why won't ownProps work similarily in the second approach as in the first?
Thank you for your time.
Not sure I have a specific answer regarding structure at the moment. As for "ownProps", that represents props that are specifically being passed in to a given component by its parent. Since you're connect()-ing FilterController, that means that "ownProps" would be coming from wherever you render that component, like: return <FilterController prop1="a" prop2={someVariable} />.
Based on how you have those map functions written, it looks like you really want to be connecting the FilterInput and FilterButton components rather than FilterController.

Categories