Extending material-ui SpeedDial component - reactjs

What I would like to do
Adding / extending existing functionality of the Speed Dial component. (refactoring into my own component)
What doesn't work
When I try to wrap the SpeedDial component inside my own component and embed it into a class or functional component it throws an error about invalid React Hooks.
how to reproduce this issue
Setup a main class or functional component
Setup a class or functional wrapper component around Material-UI SpeedDial
Implement the wrapper component inside the main (App.js) component
Results in an invalid hook call.
Dummy setup
// FAB.js
function FloatingActionButton (props) {
return (
<SpeedDial>
<SpeedDialAction />
<SpeedDialAction />
<SpeedDialAction />
</SpeedDial>
)
}
// App.js
class App extends Component {
render() {
<FloatingActionButton />
}
}
Important note
For all other components such as Button, Modal etc the above implementation just works. Probably because they do not yet implement react hooks.

Related

Reactjs : Routing to another layout from button action

I'm using React-router-dom V5 ,
Within my component , while rendering i ve to display a button , which would redirect me to another component layout :
i ve put this :
import React, { Component } from 'react';
import DashboardLayout from "../../views-layouts/DashboardLayout";
import { BrowserRouter , Switch ,Route , Link } from 'react-router-dom';
import { useHistory } from "react-router-dom";
class WelcomePage extends Component {
render() {
const heroStyles = {
padding: '50px 0 70px'
};
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<div>
WELCOME
<span />
<BrowserRouter>
<Switch>
<Route path='/home' component={DashboardLayout} />
<button type="button">
<Link to="/home">Click to login</Link>
</button>
</Switch>
</BrowserRouter>
</div>
);
}
}
export default WelcomePage;
But i ve had this error
×
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
Suggestions ??
As mentioned in the error, hooks can only be called from functional react components or other custom hooks (Rules of hooks). This is true for all hooks i.e. built-in react hooks (like useState, useEffect), custom hooks or hooks provided by third party packages
"useHistory" is a hook provided by "react-router-dom" package and you are calling it from a class component.
There is a alternative given for class components in React router documentation: https://reactrouter.com/web/api/history
In "DashboardLayout" component props, you can find "history" prop.
You can use following:
this.props.history.push("/home");
instead of
const history = useHistory();
history.push("/home");
As you are passing "DashboardLayout" as a component prop in "Route", you will get history prop in "DashboardLayout" component.
If "history" prop is not present in a particular component, then you can get it by wrapping that component with "withRouter" HOC.
https://reactrouter.com/web/api/withRouter
You're trying to call a hook in a class component. They are only able to run in functional components.

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.

Implement Redux UI State Tree?

I am trying to make all React Components' data to be available through Redux. I need it so that any component can easily modify / interact with other component.
But I don't know how to inform Redux about the fact that React Component was mounted / unmounted, because the component itself has no info about its place in the React tree. Let's say that we have a list of three React Components in our main App.js:
<div>
<ClickCounter />
<ClickCounter />
<ClickCounter />
</div>
ClickCounter is implemented here:
import React from 'react'
function ClickCounter() {
const clicks = useSelector(state => state.UI.ClickCounter[/* What is my id?*/].clicks)
return (
<div>
</div>
)
}
export default ClickCounter
This lonely ClickCounter function does not know whether it was called by the first, second or third <ClickCounter />. No info about the React tree from the component level - no synchronizing with Redux.
I stuck and don't know how to implement it. Thanks for your time in advance!

Using composition to split component into base and child in React

I have the following component that is a wrapper around ag-Grid:
<div className="ag-theme-material" style={{height: "100%"}}>
<AgGridReact
pagination
paginationPageSize={this.props.Size}
columnDefs={this.props.Col}
rowData={this.props.Row}
/>
</div>
I want to create a separate wrapper but without the pagination feature. As such I want a base class with the common features from which the two children (with and without paging) can inherit from. I know inheritance is not used much in React, so I'm wondering how to achieve the same effect using composition. Thanks!
you can use the compound components pattern, where you provide basic features for your component, and the user can use more features as he preferred.
the code would be something like this:
import React, { Component } from 'react'
import ReactDom from 'react-dom'
class AgGridReact extends Component {
static Paginator = (props list) => (...) // some jsx that represents the paginator
render() {
return <>
/** your grid jsx code here **/
// other features code
React.Children.map(this.props.children, child =>
React.cloneElement(child, {
// props you need to pass for the components
}),
)
}
</>
}
// the usage of this component will be:
<AgGridReact {...props}>
// the user can git rid of this if without paginator
<AgGridReact.Paginator />
</AgGridReact>

React Transition Group

I have been reading about React Transition Group. 95% of the material talks about CSSTransitionGroup. My understanding is that CSSTransitionGroup just builds off of TransitionGroup which simply provides callback methods that correspond to various animation events.
So I've wrapped my component up in a TransitionGroup element and given it an animation event but it is never fired.
import React, { Component } from "react";
import { TransitionGroup, Transition } from "react-transition-group";
class Test extends Component {
componentWillAppear(cb) {
console.log('componentWillAppear')
cb()
}
render() {
return <div> test </div>
}
}
class App extends Component {
render() {
return (
<TransitionGroup>
<Test />
</TransitionGroup>
)
}
}
export default App;
You can use Transition or CSSTransition without TransitionGroup, but you can't use TransitionGroup without one of the others.
From the react-transition-group docs:
The <TransitionGroup> component manages a set of <Transition> components in a list. Like with the <Transition> component, <TransitionGroup>, is a state machine for managing the mounting and unmounting of components over time.
...As items are removed or added to the TodoList the in prop is toggled automatically by the <TransitionGroup>.
Try changing your Test component's render to something like this:
render() {
return (
<Transition timeout={150}>
{(status) => (
<div className={`fade fade-${status}`}>
test
<div>
)}
</Transition>
)
}

Resources