Can't connect redux store to sematic ui dimmer components - reactjs

I am using React with semantic-ui-react library. The problem is when i use semantic-ui Dimmer and try to connect a component inside it to redux i get the error: Uncaught Invariant Violation: Could not find "store" in the context of "Connect(myComp)".
I managed to get over this error by wrapping the inner component in Provider like this:
import { Dimmer } from 'semantic-ui-react'
import { Provider } from 'react-redux'
import store from '../storelocation/store'
import SubComp from './subComp'
class App extends React.Component{
render(){
return(
<div>
<Dimmer>
<Provider store={store}>
<SubComp/>
</Provider>
</Dimmer>
</div>
)
}
}
Doing this, my SubComp is now able to connect(mstp,mdtp).
Such problem is not showing up in any other part of the app. I have wrapped my root component in Provider in my index.js like this:
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>, document.getElementById('root')
);
Now since wrapping my SubComp in provider each time i use it, fades the smile from my face, i am looking for a better option and maybe a correction if i am doing something terribly wrong here. tnx.

The problem here occurs because Semantic-UI-React uses React Portals for components like Modal, Confirm, Dimmer, Popup etc.
For more information about Portals and why this problem occurs, you may refer to this comment.
Unfortunately there seems to be no better way than wrapping your own components under Portal with a Provider via extracting store (as you have done) as of now.

Related

Can I put class base components inside function base components to use react hooks?

I have an app that was written mainly with class base react components, however we are implementing a paywall feature with Stripe.
The issue is stripe uses hooks, which don't work with class base components. Is it possible to put a child class base component into a parental function base component in order to achieve this. How would I go about doing this without rewriting every component as a function base component.
Every attempt to include a function base component with class base children always yields this error:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
Are there any work arounds, or should I re-write the all the components as functional components?
A quick example using react-router-dom for query parameters, then passing those into a class base child component.
function useQuery() {
return new URLSearchParams(useLocation().search);
}
const App = () => {
let query = useQuery();
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<Switch>
{/* Public Pages */}
<Route exact path="/" component={Landing} ref={query.get('ref')} />
</Switch>
</div>
</Router>
</Provider>
)
}
export default App;
Then assuming that the landing components is a simple class base component that will set the prop into the state.
Component's type doesn't matter in relation parent-child. But you can't use hooks inside of classBased component. Only in functional ones
Here is the code example of Functional Component as Parent and Class as Child
import React , {Component}from "react";
import "./styles.css";
const App = () => {
return (
<div className="App">
<h1>I Am Parent Functional Component</h1>
<Child />
</div>
);
};
export default App;
class Child extends Component
{
render(){
return(
<h2>I am Class Child Component</h2>
)
}
}
Okay I figured out the issue with this problem. Since I found the same question unanswered on the internet, I decided to answer the question myself, Just for future reference for anyone looking for the same solution I was running into.
You can use Hooks on a parental Function Base Component and pass on the information into a child base component.
While rendering the application, I was given an error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
However this error was due to outdated modules in npm. I just updated the react-router-dom from 4.1 to 5.2.0 npm update react-router-dom and it seem to solve the issue of using functional base component as a parent.
After updating the module I was able to use a hooks in a parental function base component and pass that on to a child class base component using props.

React: why set the document title inside useEffect?

I just watched a talk from React Conf 2018. In the video, the speaker shows 2 ways to set the document title. The first is by using the Lifecycle methods (componentDidMount and componentDidUpdate) if we use a class component, the second one is by using the useEffect hook if we use a function component. It looks like that's the recommended way to do it according to answers from this question.
But, I tested the following code and it seems to work just fine to set the document title directly:
import React from 'react';
import ReactDOM from 'react-dom';
function App() {
document.title = 'wow'
return <p>Hello</p>
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The title changed:
Is there any significance of setting the document title inside useEffect or componentDidMount?
Or is it because it was the only way to set the document title?
Is it okay to set the document title directly like I did in the snippet above?
Update:
It also works with class component:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
document.title = 'wow'
return <p>Hello</p>
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
The thing is, the render function in React, is considered to be a pure function.
A pure function should not have any side-effects. Eventually updating the document.title means that you are referencing the document directly from render-> which is already considered a side effect.
Render should mainly do one thing -> render some JSX, if there is a need to do some API calls/interact with the document/window etc..., this should be placed in componentDidMount/componentDidUpate for a class Component, OR in a useEffect/useLayoutEfect for a functional component based on hooks.
(useEffect for e.g. is a function that runs asynchronously, and updating the document title from it will not block the rendering, whereas doing it directly will block it.)
Use react-helment which is also widely used in other frameworks with the same name as helmet express ...
Here is the same code:
import {Helmet} from "react-helmet";
class Application extends React.Component {
render () {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://example.org/example" />
</Helmet>
...
</div>
);
}
};
Another way to use via props to which is cleaner IMO
...
<Helmet titleTemplate={`%s | ${props.title}`} defaultTitle={constants.defaultTitle} />
You may also use react-helmet-async for more features.

React is not rendering both modules to the DOM

I'm trying to render two modules to the DOM.
When I do an 'npm start', it runs, but it's only showing the CharacterApp part. The main app(App) doesn't show up.
Here is my index.js file code:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import CharacterApp from "./Characters/App";
import './CharacterApp/index.css';
ReactDOM.render(<App />, document.getElementById("root"));
ReactDOM.render(<CharacterApp />, document.getElementById("root"));
CharacterApp is structured like this:
function App() {
....
}
export default App
And App looks like this:
const App = () => (
....
);
export default App;
Since it's not showing errors, I'm kinda lost as to why it's not showing both on the DOM.
Is there anything I'm missing?
Thanks!
React will replace the entire content of the specified element with the render output when it is called - which is also why you should not render into document.body.
You will want to use two separate containers (perhaps create another div element and give it a unique id). Alternatively, if you don't need to have the separation of two react apps, you could instead combine the two into a single app:
ReactDOM.render(
<React.Fragment>
<App />
<CharacterApp />
</React.Fragment>,
document.getElementById('root')
)
How can it render two modules ? you are rendering the App module in a div with id='root' and then you are overwriting it with another module. Its the correct behaviour you can not render two modules like that.
If you want to render both modules in the same page, create a separate component and import your App and CharacterApp in that module and then render that new component in the dom i.e
ReactDOM.render( <YourNewComponent /> ,
document.getElementById('root')
)
And if you do not want to create a seperate component, you can just simple do this.
ReactDOM.render(
<React.Fragment>
<App />
<CharacterApp />
</React.Fragment>,
document.getElementById('root')
)
`ReactDOM.render(
<React.Fragment>
<App />
<CharacterApp />
</React.Fragment>, document.getElementById('root'))`
> this will work. you can not use same 'root' for rendering 2
> components. instead you can group them in one or use different div
> Ids.
This code is the culprit:
ReactDOM.render(<App />, document.getElementById("root"));
ReactDOM.render(<CharacterApp />, document.getElementById("root"));
You're using the same div for showing your apps, so the second statement is replacing the App component with CharacterApp.
You can create two divs in index.html, say, div#root-app and div#root-character-app, and then render the two components to the two containers respectively.

using the Router component inside of a function react

I have a react component called react-dropzone. It allows you to drag and drop files, which then invokes a function (usually you "do" something with the file, but you can have it invoke any function you please).
When a file is dropped, I want to route the client to 'page-redirect'. The following code has no errors, but does nothing.
import React, { Component } from "react";
import Dropzone from "react-dropzone";
import { BrowserRouter as Router, Route } from "react-router-dom";
import PageRedirect from "./pages/page-redirect.jsx";
class DragAndDrop extends Component {
//function invoked by the Dropzone Component
handleOnDrop = files => {
console.log(files);
return (
<Router>
<Route exact path="/pages/page-redirect" component={PageRedirect} />
</Router>
);
};
render() {
return (
<div>
<Dropzone onDrop={this.handleOnDrop}> Drop File Here </Dropzone>
</div>
);
}
}
export default DragAndDrop;
Any insight is greatly appreciated!
Your code returns nothing because you are essentially defining a new route not going to it.
If you want to just open the redirect page in another tab, you can just insert the following after you log the files.
window.open("/pages/pages-redirect")
or you could do:
window.location.href("/pages/pages-redirect") //please ignore this and see below for the update
This assumes that you have already defined your route in your root js file, i.e. your index.js or App.js or even your routes directory (if your using one). If you haven't define your route first wherever you have your other routes defined.
Hope that helps...
Update
The href will not work the way I have it above in React. You should change that to:
window.location.href = "/pages/pages-redirect"
If you received a "window.location.href is not a function" error that is why. Sorry about that. Hope that helps anyone that got an error.
Also, the .open will open in a new tab and the href will redirect to that path.
You have to define your Route before navigating to it. Then you can use Link component or use history object to navigate to the Route.
So your component would look something like this:
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import PageRedirect from "./pages/page-redirect.jsx";
import Dropzone from "react-dropzone";
const Parent = (props) => (
<Router>
<Route exact path="/pages/page-redirect" component={PageRedirect} />
<Route exact path="/draganddrop" component={DragAndDrop} />
</Router>
);
class DragAndDrop extends Component {
//function invoked by the Dropzone Component
handleOnDrop = files => {
console.log(files);
this.props.history.push("/pages/page-redirect");
};
render() {
return (
<div>
<Dropzone onDrop={this.handleOnDrop}> Drop File Here </Dropzone>
</div>
);
}
}
export default DragAndDrop;
You can separate DragAnDrop and Parent as you want.
The tag just defines what component will render for a particular route, it doesn't redirect. To redirect to a certain location you should use <Redirect> from 'react-router' and then conditionally render <Redirect to="/pages/page-redirect"/>.
Also, to accomplish this redirection you need to define this route( and /or any other route) using BrowserRouter as you've done( preferably in a base file like App.js.).
This is a great article about React-Router: https://medium.com/#pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf
While this will give you an insight about <Redirect>: https://medium.com/#anneeb/redirecting-in-react-4de5e517354a
Hope this helps. If you would like to have a sample code for your problem feel absolutely free to ask. But be sure to read the above articles and try yourself if you can. Thanks

Create modal in react-redux using stateless functional components

We are implementing an application in React-Redux with stateless functional components and containers for the ones that need to handle state. The idea is to have all information in the Redux store.
In the application we need to show in several places different modal windows. I have looked into the articles here in stack overflow, but I cannot get this working. I have followed the process described by Dan Abramov in this post.
My main question is where I should render my ModalRoot component inside my application. The process is the following:
I have a button that dispatches an action displayModal in order to display a modal...
clickHandler: modalProps => (
dispatch(displayModal('MODAL_1', modalProps))
export function displayModal(modalType, modalProps) {
return {
type: DISPLAY_MODAL,
payload: {
modalType,
modalProps
}
};
}
Then the action gets handled by a reducer that sets the modalType property in the redux-state...
export default function (modal = Map(), action) {
if (!action) {
return modal;
}
switch (action.type) {
case DISPLAY_MODAL:
return modal
.set('modalType', action.payload.modalType)
.set('modalProps', action.payload.modalProps);
default:
return modal;
}
}
Then I have the ModalRootContainer that passes the state to my ModalRoot component...
const mapStateToProps = state => (
{
modalType: modalState(state).get('modalType'),
modalProps: modalState(state).get('modalProps')
}
);
const ModalRootContainer =
connect(mapStateToProps)(ModalRoot);
export default ModalRootContainer;
ModalRootComponent is as follows:
const ModalRoot = (modalType, modalProps) => {
if (!modalType) {
return &ltspan />;
}
switch (modalType) {
case 'MODAL_1':
return &ltMyComponent {...modalProps}/>;
default:
return &ltspan />;
}
};
ModalRoot.propTypes = {
modalType: PropTypes.string,
modalProps: PropTypes.object
};
export default ModalRoot;
My understanding is that this is correct but it does not work. Where should ModalRoot be placed? Inside the Provider tag? What am I missing?
Edit 1:
I am trying to use what Random User is saying in his answer below with no effect. So I have in my index.html:
<body>
<div id="modals"></div>
<div id="root"></div>
</body>
In my main.js:
ReactDOM.render(
<Provider store={store}>
<Router history={hashHistory}>{routing(store)}</Router>
</Provider>,
document.getElementById('root')
);
ReactDOM.render(
<Provider store={store}>
<ModalRootContainer />
</Provider>,
document.getElementById('modals')
);
And with all the above setup still no modal is being shown. I debug and I see that the action is triggered and the state is updated from the reducer. Is there a way to debug after this point to see if something is wrong with my container and why is the modal not shown?
Edit 2: I have found what was wrong. I was passing wrongly the state to my container. The code seems to work, however now instead of having a modal window show, I am having my component (supposed to be inside the modal) appear above my remaining page. I am just rendering for the moment a simple text "Close modal" and a button to close it. It is shown and hidden correctly but it does not work as a modal, it just shows and hides. Any help on what I am missing?
You should render ModalRootContainer inside your current Provider. You probably have some app.js file with <Provider> ... </Provider> inside. This is the place where you should render your container.
It's because redux shares state through context (https://facebook.github.io/react/docs/context.html). Provider is a component that creates that context and thanks to connect you have its data in props. So to have access to state you must render all containers as children/grandchildren/... of Provider component.
You have declared a ModalRootComponent but to actually use it you have to render it somewhere in your application. What I mean by render is to put it inside reacts ReactDOM.render(...) function.
When you are using redux your rendering function will usually look like this with Provider tag on top of everything:
ReactDOM.render(
<Provider store={store}>
...
</Provider>,
document.getElementById('root')
);
What you can do to render the ModalRootContainer component is just to put it inside the Provider tag like this:
ReactDOM.render(
<Provider store={store}>
<ModalRootContainer />
</Provider>,
document.getElementById('root')
);
But in a more complex scenario you would go with rendering the router here and then inside each route you would actually do the content:
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
{routes}
</Router>
</Provider>,
document.getElementById('root')
);
Also make sure that you have set up redux store right and it knows about your reducer. You can see working todo example with react and redux here.

Resources