const in class react i18n [duplicate] - reactjs

In this example, I have this react class:
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
The question is if I can add React hooks to this. I understand that React-Hooks is alternative to React Class style. But if I wish to slowly migrate into React hooks, can I add useful hooks into Classes?

High order components are how we have been doing this type of thing until hooks came along. You can write a simple high order component wrapper for your hook.
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useMyHook();
return <Component {...props} myHookValue={myHookValue} />;
}
}
While this isn't truly using a hook directly from a class component, this will at least allow you to use the logic of your hook from a class component, without refactoring.
class MyComponent extends React.Component {
render(){
const myHookValue = this.props.myHookValue;
return <div>{myHookValue}</div>;
}
}
export default withMyHook(MyComponent);

Class components don't support hooks -
According to the Hooks-FAQ:
You can’t use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.

As other answers already explain, hooks API was designed to provide function components with functionality that currently is available only in class components. Hooks aren't supposed to used in class components.
Class components can be written to make easier a migration to function components.
With a single state:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { state } = this;
const setState = state => this.setState(state);
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [state, setState] = useState({sampleState: 'hello world'});
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
Notice that useState state setter doesn't merge state properties automatically, this should be covered with setState(prevState => ({ ...prevState, foo: 1 }));
With multiple states:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { sampleState } = this.state;
const setSampleState = sampleState => this.setState({ sampleState });
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}

Complementing Joel Cox's good answer
Render Props also enable the usage of Hooks inside class components, if more flexibility is needed:
class MyDiv extends React.Component {
render() {
return (
<HookWrapper
// pass state/props from inside of MyDiv to Hook
someProp={42}
// process Hook return value
render={hookValue => <div>Hello World! {hookValue}</div>}
/>
);
}
}
function HookWrapper({ someProp, render }) {
const hookValue = useCustomHook(someProp);
return render(hookValue);
}
For side effect Hooks without return value:
function HookWrapper({ someProp }) {
useCustomHook(someProp);
return null;
}
// ... usage
<HookWrapper someProp={42} />
Source: React Training

you can achieve this by generic High order components
HOC
import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => {
return function WrappedComponent(props) {
const hookValue = useHook();
return <Component {...props} {...{[hookName]: hookValue}} />;
};
};
export default withHook;
Usage
class MyComponent extends React.Component {
render(){
const myUseHookValue = this.props.myUseHookValue;
return <div>{myUseHookValue}</div>;
}
}
export default withHook(MyComponent, useHook, 'myUseHookValue');

Hooks are not meant to be used for classes but rather functions. If you wish to use hooks, you can start by writing new code as functional components with hooks
According to React FAQs
You can’t use Hooks inside of a class component, but you can
definitely mix classes and function components with Hooks in a single
tree. Whether a component is a class or a function that uses Hooks is
an implementation detail of that component. In the longer term, we
expect Hooks to be the primary way people write React components.
const MyDiv = () => {
const [sampleState, setState] = useState('hello world');
render(){
return <div>{sampleState}</div>
}
}

You can use the react-universal-hooks library. It lets you use the "useXXX" functions within the render function of class-components.
It's worked great for me so far. The only issue is that since it doesn't use the official hooks, the values don't show react-devtools.
To get around this, I created an equivalent by wrapping the hooks, and having them store their data (using object-mutation to prevent re-renders) on component.state.hookValues. (you can access the component by auto-wrapping the component render functions, to run set currentCompBeingRendered = this)
For more info on this issue (and details on the workaround), see here: https://github.com/salvoravida/react-universal-hooks/issues/7

Stateful components or containers or class-based components ever support the functions of React Hooks, so we don't need to React Hooks in Stateful components just in stateless components.
Some additional informations
What are React Hooks?
So what are hooks? Well hooks are a new way or offer us a new way of writing our components.
Thus far, of course we have functional and class-based components, right? Functional components receive props and you return some JSX code that should be rendered to the screen.
They are great for presentation, so for rendering the UI part, not so much about the business logic and they are typically focused on one or a few purposes per component.
Class-based components on the other hand also will receive props but they also have this internal state. Therefore class-based components are the components which actually hold the majority of our business logic, so with business logic, I mean things like we make an HTTP request and we need to handle the response and to change the internal state of the app or maybe even without HTTP. A user fills out the form and we want to show this somewhere on the screen, we need state for this, we need class-based components for this and therefore we also typically use class based components to orchestrate our other components and pass our state down as props to functional components for example.
Now one problem we have with this separation, with all the benefits it adds but one problem we have is that converting from one component form to the other is annoying. It's not really difficult but it is annoying.
If you ever found yourself in a situation where you needed to convert a functional component into a class-based one, it's a lot of typing and a lot of typing of always the same things, so it's annoying.
A bigger problem in quotation marks is that lifecycle hooks can be hard to use right.
Obviously, it's not hard to add componentDidMount and execute some code in there but knowing which lifecycle hook to use, when and how to use it correctly, that can be challenging especially in more complex applications and anyways, wouldn't it be nice if we had one way of creating components and that super component could then handle both state and side effects like HTTP requests and also render the user interface?
Well, this is exactly what hooks are all about. Hooks give us a new way of creating functional components and that is important.

React Hooks let you use react features and lifecycle without writing a class.
It's like the equivalent version of the class component with much smaller and readable form factor. You should migrate to React hooks because it's fun to write it.
But you can't write react hooks inside a class component, as it's introduced for functional component.
This can be easily converted to :
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div>{sampleState}</div>
}

It won't be possible with your existing class components. You'll have to convert your class component into a functional component and then do something on the lines of -
function MyDiv() {
const [sampleState, setSampleState] = useState('hello world');
return (
<div>{sampleState}</div>
)
}

For me React.createRef() was helpful.
ex.:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
...
<FunctionComponent ref={this.myRef} />
Origin post here.

I've made a library for this. React Hookable Component.
Usage is very simple. Replace extends Component or extends PureComponent with extends HookableComponent or extends HookablePureComponent. You can then use hooks in the render() method.
import { HookableComponent } from 'react-hookable-component';
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
class ComponentThatUsesHook extends HookableComponent<Props, State> {
render() {
// πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡
const value = useSomeHook();
return <span>The value is {value}</span>;
}
}

if you didn't need to change your class component then create another functional component and do hook stuff and import it to class component

Doesn't work anymore in modern React Versions. Took me forever, but finally resulted going back to go ol' callbacks. Only thing that worked for me, all other's threw the know React Hook Call (outside functional component) error.
Non-React or React Context:
class WhateverClass {
private xyzHook: (XyzHookContextI) | undefined
public setHookAccessor (xyzHook: XyzHookContextI): void {
this.xyzHook = xyzHook
}
executeHook (): void {
const hookResult = this.xyzHook?.specificHookFunction()
...
}
}
export const Whatever = new WhateverClass() // singleton
Your hook (or your wrapper for an external Hook)
export interface XyzHookContextI {
specificHookFunction: () => Promise<string>
}
const XyzHookContext = createContext<XyzHookContextI>(undefined as any)
export function useXyzHook (): XyzHookContextI {
return useContext(XyzHookContextI)
}
export function XyzHook (props: PropsWithChildren<{}>): JSX.Element | null {
async function specificHookFunction (): Promise<void> {
...
}
const context: XyzHookContextI = {
specificHookFunction
}
// and here comes the magic in wiring that hook up with the non function component context via callback
Whatever.setHookAccessor(context)
return (
< XyzHookContext.Provider value={context}>
{props.children}
</XyzHookContext.Provider>
)
}
Voila, now you can use ANY react code (via hook) from any other context (class components, vanilla-js, …)!
(…hope I didn't make to many name change mistakes :P)

Yes, but not directly.
Try react-iifc, more details in its readme.
https://github.com/EnixCoda/react-iifc

Try with-component-hooks:
https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';
class MyComponent extends React.Component {
render(){
const props = this.props;
const [counter, set] = React.useState(0);
//TODO...
}
}
export default withComponentHooks(MyComponent)
2.Try react-iifc: https://github.com/EnixCoda/react-iifc

Related

Accessing functional component's methods from the 'outside'

Let's say that I want to control the loading state of my app by calling the method from outside of the app like so:
setLoading(true)
I have implemented a functional component for which something like that works:
import React from 'react';
function App() {
const [loading, setLoading] = React.useState(true);
window.setLoading = (isLoading) => { setLoading(isLoading) };
if (loading) return 'Loading...';
return 'App content';
}
I believe however that mapping the setLoading() to window is not the best approach. Is it possible then to do it differently?
If I had a class component, it would look like this:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
}
}
setLoading = (loading) => {
this.setState({ loading });
}
render() {
return(this.state.loading ? 'Loading...' : 'App content');
}
}
Then upon rendering I would use callback ref to make the whole component with all it's methods available.
<App ref={(app) => { window.app = app }} />
app.setLoading(true)
This approach also pollutes the global scope but it's more concise - the component is exposed as a whole.
As neither of the approaches is optimal, which one should I use? Is there maybe something better?
I think exposing only the required functions to the outside is more concise, compared to exposing the whole component. If you're exposing to the outside anyway, having a React component exposed outside the context of React does not make much sense - instead, something like window.setAppLoading seems more like an external function for controlling the app's state, which is probably what you need.
Basically, if you want to expose a certain function controlling something about your app to the outside, it is cleaner to expose only that, instead of exposing a part of your app's internals.

Const = () => vs Class Functions function name() {} in React Native

I'm new to react native, I'm bit confused about components.
As I created first react native app I saw App.js the components in App.js created as per following code:
export default function App() {
...
}
and as I saw tutorials many people almost all people making components as per following code:
const FirstComponents = () => {
...
}
I'm also confuse about function components and class based components which created as per following code:
export default class FirstComponents extends Components(){
...
}
What is the difference between function and class base components?
Please provide me answer with examples. Your time will be appreciated.
In javascript there are multiple ways to create a function. For example:
function myFunction () {
//some action
}
const myFunction = () => {
//some action
}
These two are functions and makes the same thing.
Now second part of your question is "What is the difference between functional and class based components?"
Class based components used for controlling your state and for lifecycle methods(ComponentDidMount and etc...) in the past. And if you are not using state in your component or lifecyle methods, you would use functional based component. Basically if you have a small component with only some UI things, it was best to use functional components. However with React version 16.8 React team intruduced hooks.
Hooks provides the same concepts with your state and component lifecyle methods and more. With hooks you can control your component even if they are funcitonal components.
The first two snippets are similar in terms of declaration. Both are functional components. These are different from class based components. There are several differences:
Hooks can be used in only functional component, class based can't.
constructor is used to initialize this in the class component but in functional you don't require this.
Lifecycle hooks are not available in the functional component, they are part of class component.
You can use useEffect hook for a lifecycle hook like componentDidMount.
For a short example:
function App(){
const [count, setCount] = useState('');
}
In the above example "count" is a local state property of component and setCount is a method which updates the state.
class App extends React.Component{
constructor(props){
super(props);
this.state = { count: 0 };
this.increaseCount = this.increaseCount.bind(this);
}
increaseCount(){
this.setState({count: this.count+1});
}
// lifecycle methods like componentDidMount, componentDidUpdate etc.
render(){
return(
<button onClick={this.increaseCounter}>INCR</button>
);
}
}
In this class component you can see state is defined in the constructor and it is been updated with the setState method.
real time example will be too much to add, rather i suggest you to take simple examples to have a grasp of the concepts.

How to change state of component from anywhere without Redux?

Is this bad practices or not ?
export state change function from component
import it from other file.
call the function to change state?
In this way we can change some component state from anywhere.
For example...
We want to change the Model.js state from anywhere.
Modal.js
import React from 'react';
export let toggleModal;
export default class Modal extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
};
toggleModal = this.toggleModal;
}
toggleModal = () => {
this.setState({ open: !this.state.open });
};
render() {
const { open } = this.state;
return <div style={{ color: 'red' }}>{open && 'Hello Modal'}</div>;
}
}
App.js(Some Top Level component)
import React from 'react';
import Modal from './Modal';
export default () => (
<>
...
<Modal />
...
</>
);
Somewhere.js
import React from 'react';
import {toggleModal} from './Modal';
export default () => (
<>
<h1>Hello!</h1>
<button onClick={() => toggleModal()}>open Modal!</button>
</>
);
γ€€γ€€
But there is no reference in React Official docs, so is this bad practices ?
What React Docs recommends...
Just passing function props to change parent state from parent to children
Use context
Redux or Mobx
But, these are too complex for me.
Example code here
https://next.plnkr.co/edit/37nutSDTWp8GGv2r?preview
Everything seems pretty much overwhelming and difficult at the beginning. But as we get out hands on them, it's give us more confidence to dig into.
I would recommend to use redux that's how we tackled props drilling problem. You can dispatch a action and connect reducer to corresponding component which upon updating state will re render. This is what I recommend to most of the people to learn the tale of redux with a real life example:
Understanding Redux: The World’s Easiest Guide to Beginning Redux
Apart from this you can take Dan Abramov, author of the library, free redux course on egghead.io:
Getting Started with Redux
The problem you run into, almost immediately like your code example does is this:
It will not work: your toggleModal() method expects a this to refer to an actual component instance. When your onClick() handler fires you invoke toggleModal() as a plain function. The this context will be wrong, and so at best (in your example) you will get an error because you try to invoke something undefined, at worst (in general) you end up invoking the wrong method.
When you think about it, for any non-trivial React component you will have a hard time obtaining a reference to the actual instance that is currently being used: you have to make sure that you are not forgetting to invoke the method on the right component instance and also you have to consider that instances may be created/destroyed 'at will' for whatever reason. For example: what if your component is rendered indirectly as part of some other component's render() method? Multiple layers of indirection like that make it even harder.
Now, you could fix all that by abusing ref with abandon but you will find that now you have to keep track of which ref refers to what particular instance, if you happen to have multiple of the components to consider in one render tree...
Whenever you think one component needs to handle the state of its siblings, the solution is usually to lift the state one level up.
export default class Modal extends React.Component {
render() {
const { isOpen } = this.props;
return <div style={{ color: 'red' }}>{isOpen && 'Hello Modal'}</div>;
}
}
export default class Home {
this.state = {
isOpen: false,
};
toggleModal = () => {
this.setState({ isOpen: !this.state.isOpen });
}
render() {
const { isOpen } = this.state;
return (
<>
<h1>Hello {name}!</h1>
<button onClick={() => this.toggleModal()}>open Modal!</button>
<Modal isOpen={isOpen}/>
<p>Start editing and see your changes reflected here immediately!</p>
</>
)
}
}
This way the Home handle the state and your problem is solved.
This can get annoying if the state needs to be "drilled down" to children, that's a problem than redux or react-context can solve.
Here <Modal /> is the child component. So to call a function in a child component you can simply use Ref.
You can refer this page to get more info about Ref.
You can assign a class variable as a ref to this child and use this class variable as an object to call its function.
I found if in special case, my way is okay.
Special case means something like customAlert component.
It is okay only one instance of customAlert component mounted at a time in App.
To achieve this...
1.Use ref to access and change DOM
2.attach state changing function or component to window and call window.function
3.my case: export state changing function and import it from other file.
And here is how to do with react Context
https://next.plnkr.co/edit/EpLm1Bq3ASiWECoE?preview
I think Redux is overkill if the main thing you are interested in is to make some states-like data available and updatable throughout your App without props drilling.
For that purpose, a much simpler approach (maybe not available at the time the question was posted?) is to use react context: https://frontend.turing.edu/lessons/module-3/advanced-react-hooks.html
"context - an API given to us by React, allowing for the passing of
information to child components without the use of props
[...]
useContext - a react hook, allowing functional components to take
advantage of the context API"

react, differences between component create with extend class and simple const = function

In react tutorial:
https://egghead.io/lessons/javascript-redux-react-todo-list-example-filtering-todos
there is main component create with extends:
class TodoApp extends Component {
render() {
const visibleTodos = getVisibleTodos(
this.props.todos,
this.props.visibilityFilter
);
.
. // Input and Button stuff
.
and another components just create like a const that hold a function:
const FilterLink = ({
filter,
children
}) => {
return (
<a href='#'
onClick={e => {
e.preventDefault();
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter
});
}}
>
{children}
</a>
)
}
the difference i see, first created with class use a render function, and the another a return function to send back the template.
What are the differences? I've heard components in the future only will be allowed with extend Component ?
See: React.createClass vs. ES6 arrow function
The short answer is that you want to use Stateless Functional Components (SFC) as often as you can; the majority of your components should be SFC's.
Use the traditional Component class when:
You need local state (this.setState)
You need a lifecycle hook (componentWillMount, componentDidUpdate, etc)
You need to access backing instances via refs (eg. <div ref={elem => this.elem = elem}>)
class Foo extends Component {} results in components with some React lifecycle hooks, while const Foo = (props) => {} does not. The latter should therefore result in more performant code and would be considered a "stateless" or "pure" component, as it doesn't come with any additional baggage.

Can react-redux connect() -ed containers implement lifecyle methods like componentDidMount?

I've come across a repeated pattern in my react-redux site:
A component displays data from a web api, and it should be populated on load, automatically, without any user interaction.
I want to initiate the async fetch from a container component, but as far as I can tell the only way to do it is from a lifecycle event in a display component. This seems to make it impossible to put all the logic in the container and only use dumb stateless functional components for display.
This means I can't use a stateless functional component for any component that needs async data. That doesn't seem right.
It seems like the "right" way to do this would be to somehow initiate async calls from the container. Then when the call returned, the state would be updated and the container would get the new state and would in turn pass those to its stateless component via mapStateToProps().
Making async calls in mapStateToProps and mapDispatchToProps (I mean actually calling the async function, as opposed to returning it as a property) doesn't make sense.
So what I've ended up doing is putting the async call(s) in a refreshData() function exposed by mapDispatchToProps(), then calling it from two or more of the React lifecycle methods: componentDidMount and componentWillReceiveProps.
Is there a clean way to update the redux store state without putting lifecycle method calls in every component that needs async data?
Should I be making these calls higher up the component heierarchy (thereby reducing the scope of this issue, since only "top-level" components would need to listen to lifecycle events)?
Edit:
Just so there's no confusion what I mean by a connect()ed container component, here's a very simple example:
import React from 'react';
import { connect } from 'react-redux';
import {action} from './actions.js';
import MyDumbComponent from './myDumbComponent.jsx';
function mapStateToProps(state)
{
return { something: state.xxxreducer.something };
}
function mapDispatchToProps(dispatch)
{
return {
doAction: ()=>{dispatch(action())}
};
}
const MyDumbComponentContainer = connect(
mapStateToProps,
mapDispatchToProps
)(MyDumbComponent);
// Uh... how can I hook into to componentDidMount()? This isn't
// a normal React class.
export default MyDumbComponentContainer;
Jamie Dixon has written a package to do this!
https://github.com/JamieDixon/react-lifecycle-component
Usage would look like this:
const mapDispatchToProps = {
componentDidMount: getAllTehDatas
}
...
export default connectWithLifecycle(mapStateToProps, mapDispatchToProps)(WrappedComponent)
edit
With hooks you are now able to implement lifecycle callbacks in a stateless functional component. While this may not directly address all of the points in the question, it may also get around some of the reason for wanting to do what was originally proposed.
edit to original answer
After the discussion in comments and thinking about it more, this answer is more exploratory and can serve as a piece of the conversation. But I don't think it's the right answer.
original answer
On the Redux site there's an example that shows you don't have to do both mapStateToProps and mapDispatchToProps. You can just leverage connect's awesomeness for the props, and use a class and implement the lifecycle methods on the dumb component.
In the example, the connect call is even in the same file and the dumb component isn't even exported, so to the user of the component it's looks the same.
I can understand not wanting to issue async calls from the display component. I think there's a distinction between issuing the async calls from there and dispatching an action which, with thunks, moves the issuing of the async calls up into the actions (even more decoupled from the React code).
As an example, here's a splash screen component where I'd like to do some async action (like asset preloading) when the display component mounts:
SplashContainer.js
import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'
const mapStateToProps = (state) => {
return {
// whatever you need here
}
}
const mapDispatchToProps = (dispatch) => {
return {
onMount: () => dispatch(actions.splashMount())
}
}
const SceneSplash = connect(
mapStateToProps,
mapDispatchToProps
)(Splash)
export default SceneSplash
Splash.js
import React from 'react'
class Splash extends React.Component {
render() {
return (
<div className="scene splash">
<span className="fa fa-gear fa-spin"></span>
</div>
)
}
componentDidMount() {
const { onMount } = this.props
onMount()
}
}
export default Splash
You can see the the dispatch happens in the connected container, and you can imagine in the actions.splashMount() call we issue an async http request or do other async things via thunks or promises.
edit to clarify
Allow me to try to defend the approach. I re-read the question and am not 100% sure I'm addressing the main thing it's after, but bear with me. If I am still not quite on track, I have a modified approach below that may be closer to the mark.
"it should be populated on load" - the example above accomplishes this
"I want to initiate the async fetch from a container" - in the example it's not initiated from the display component or the container, but from an async action
"This seems to make it impossible to put all the logic in the container" - I think you can still put any additional logic needed in the container. As noted, the data loading code isn't in the display component (or the container) but in the async action creator.
"This means I can't use a stateless functional component for any component that needs async data." - in the above example the display component is stateless and functional. The only link is the lifecycle method invoking a callback. It need not know or care what that callback does. It is not a case of the display component trying to be the owner of async data fetching - it's merely letting the code that does handle that know when a particular thing has happened.
So far I'm attempting to justify how the example given meets the question's requirements. That said, if what you're after is having a display component that contains absolutely no code related to the async data load even by indirect callbacks - that is, the only link it has is to consume that data via the props it's handed when that remote data comes down, then I would suggest something like this:
SplashContainer.js
import { connect } from 'react-redux'
import Splash from '../components/Splash'
import * as actions from '../actions'
const mapStateToProps = (state) => {
return {
// whatever you need here
}
}
const mapDispatchToProps = (dispatch) => {
dispatch(actions.splashMount())
return {
// whatever else here may be needed
}
}
const SceneSplash = connect(
mapStateToProps,
mapDispatchToProps
)(Splash)
export default SceneSplash
Splash.js
import React from 'react'
class Splash extends React.Component {
// incorporate any this.props references here as desired
render() {
return (
<div className="scene splash">
<span className="fa fa-gear fa-spin"></span>
</div>
)
}
}
export default Splash
By dispatching the action in mapDispatchToProps you are letting the code for that action reside entirely in the container. In fact, you are starting the async call as soon as the container is instantiated, rather than waiting for the connected display component to spin up and be mounted. However, if you cannot begin the async call until the componentDidMount() for the display component fires, I think you're inherently bound to have code like in my first example.
I haven't actually tested this second approach to see if react or redux will complain about it, but it should work. You have access to the dispatch method and should be able to call it without problems.
To be honest, this second example, while removing all code related to the async action from the display component does kind of strike me as a bit funny since we're doing non-mapping-of-dispatch-to-props things in the eponymous function. And containers don't actually have a componentDidMount to run it in otherwise. So I'm a bit squirmy with it and would lean toward the first approach. It's not clean in the "feels right" sense, but it is in the "simple 1-liner" sense.
Check out redux-saga https://github.com/yelouafi/redux-saga. It's a redux middleware component that creates long-lived watchers that look for specific store actions and can trigger functions or generator functions in response. The generator syntax is particular nice for handling async, and redux-saga has some nice helpers that allow you to treat async code in a synchronous fashion. See some of their examples. https://github.com/yelouafi/redux-saga/blob/master/examples/async/src/sagas/index.js . The generator syntax can be hard to grok at first, but based on our experience this syntax supports extremely complex async logic, including debounce, cancellation and joining/racing multiple requests.
You can do it from a container. Just make a component that extends React.Component but name it with "Container" somewhere in the name. Then use that container's componentDidMount instead of using componentDidMount in the presentational (dumb) component that the container component renders. Reducer will see that you've dispatched an action still, and still update state so your dumb component will be able to get at that data..
I TDD but even if I didn't TDD I separate out my dumb vs container components via file. I hate having too much in one file, especially if mixing dumb vs. container stuff in the same file, that's a mess. I know people do it, but I think that's awful.
I do this:
src/components/someDomainFolder/someComponent.js (dumb component)
src/components/someDomainFolder/someComponentContainer.js (for example you might use React-Redux..have connected container not a connected presentational component..and so in someComponentContainer.js you DO have a react class in this file, as stated, just call it someComponentContainer extends React.Component for example.
Your mapStateToProps() and mapDispatchToProps() would be global functions of that connected container component outside that container class. And connect() would render the container, which would render the presentational component but this allows you to keep all your behavior in your container file, away from dumb presentational component code.
that way you have tests around someComponent that are structural/state based and you have behavior tests around the Container component. Much better route to be going with maintaining and writing tests, and maintaining and making things easy for yourself or other devs to see what's going on and to manage dumb vs. behavioral components.
Doing things this way, your presentational stuff is separated both physically by file AND by code convention. AND your tests are grouped around the right areas of code...not an intermingled mess. AND if you do this, and use a reducer that listens to update state, your presentational component can remain totally stupid....and just look for that updates state via props...since you're using mapStateToProps().
Following up on #PositiveGuy suggestion, here is example code of how to implement a container component that can utilize lifecycle methods. I think this is a pretty clean approach that maintains separation of concerns keeping the presentation component "dumb":
import React from 'react';
import { connect } from 'react-redux'
import { myAction } from './actions/my_action_creator'
import MyPresentationComponent from './my_presentation_component'
const mapStateToProps = state => {
return {
myStateSlice: state.myStateSlice
}
}
const mapDispatchToProps = dispatch => {
return {
myAction: () => {
dispatch(myAction())
}
}
}
class Container extends React.Component {
componentDidMount() {
//You have lifecycle access now!!
}
render() {
return(
<MyPresentationComponent
myStateSlice={this.props.myStateSlice}
myAction={this.props.myAction}
/>
)
}
}
const ContainerComponent = connect(
mapStateToProps,
mapDispatchToProps
)(Container)
export default ContainerComponent
You CAN initiate an async fetch from the parent container (the smart container). You write the function in the smart container and you pass the function as a prop for the dumb container. For example:
var Parent = React.createClass({
onClick: function(){
dispatch(myAsyncAction());
},
render: function() {
return <childComp onClick={this.onClick} />;
}
});
var childComp = React.createClass({
propTypes:{
onClick: React.PropTypes.func
},
render: function() {
return <Button onClick={this.props.onClick}>Click me</Button>;
}
});
childComp is stateless since the onClick definition is determined by the parent.
EDIT: Added connected container example below, excluded other stuff for brevity. Doesn't really show much, and it's a bit cumbersome to setup on fiddle and stuff, point is, I do use lifecycle methods in connected containers and it works out fine for me.
class cntWorkloadChart extends Component {
...
componentWillReceiveProps(nextProps){
if(nextProps.myStuff.isData){
if (nextProps.myStuff.isResized) {
this.onResizeEnd();
}
let temp = this.updatePrintingData(nextProps)
this.selectedFilterData = temp.selectedFilterData;
this.selectedProjects = temp.selectedProjects;
let data = nextProps.workloadData.toArray();
let spread = [];
if(nextProps.myStuff.isSpread) {
spread = this.updateSelectedProjectSpread(nextProps);
for (var i = 0; i < data.length; i++) {
data[i].sumBillableHrsSelectedProjects = spread[data[i].weekCode] ? Number(spread[data[i].weekCode].sumBillableHrsSelectedProjects.toFixed(1)) : 0;
data[i].sumCurrentBudgetHrsSelectedProjects = spread[data[i].weekCode] ? Number(spread[data[i].weekCode].sumCurrentBudgetHrsSelectedProjects.toFixed(1)) : 0;
data[i].sumHistoricBudgetHrsSelectedProjects = spread[data[i].weekCode] ? Number(spread[data[i].weekCode].sumHistoricBudgetHrsSelectedProjects.toFixed(1)) : 0;
}
}
if (nextProps.potentialProjectSpread.length || this.props.potentialProjectSpread.length) { //nextProps.myStuff.isPpSpread) { ???? - that was undefined
let potential = nextProps.potentialProjectSpread;
let ppdd = _.indexBy(potential, 'weekCode');
for (var i = 0; i < data.length; i++) {
data[i].sumSelectedPotentialProjects = ppdd[data[i].weekCode] ? ppdd[data[i].weekCode].sumSelectedPotentialProjects.toFixed(1) : 0;
}
}
for (var i = 0; i < data.length; i++) {
let currObj = data[i];
currObj.sumCurrentBudgetHrs = currObj.currentBudgeted.sumWeekHours;
currObj.sumHistoricBudgetHrs = currObj.historicBudgeted.sumWeekHours;
currObj.fillAlpha = .6; //Default to .6 before any selections are made
//RMW-TODO: Perhaps we should update ALL line colors this way? This would clean up zero total bars in all places
this.updateLineColor(currObj, "sumSelectedPotentialProjects", "potentialLineColor", potentialLineColor);
this.updateLineColor(currObj, "sumHistoricBudgetHrs", "histLineColor", histBudgetLineColor);
this.updateLineColor(currObj, "sumHistoricBudgetHrsSelectedProjects", "histSelectedLineColor", selectedHistBudgetFillColor);
}
if(nextProps.myStuff.isSelectedWeek){
let currWeekIndex = nextProps.weekIndex.index;
let selectedWeek = data[currWeekIndex].fillAlpha = 1.0;
}
if(data.length > 0){
if(data[0].targetLinePercentages && data.length > 9) { //there are target lines and more than 10 items in the dataset
let tlHigh = data[0].targetLinePercentages.targetLineHigh;
let tlLow = data[0].targetLinePercentages.targetLineLow;
if (tlHigh > 0 && tlLow > 0) {
this.addTargetLineGraph = true;
this.upperTarget = tlHigh;
this.lowerTarget = tlLow;
}
}
else {
this.addTargetLineGraph = false;
this.upperTarget = null;
this.lowerTarget = null;
}
}
this.data = this.transformStoreData(data);
this.containsHistorical = nextProps.workloadData.some(currObj=> currObj.historicBudgeted.projectDetails.length);
}
}
...
render() {
return (
<div id="chartContainer" className="container">
<WorkloadChart workloadData={this.props.workloadData}
onClick={this.onClick}
onResizeEnd={this.onResizeEnd}
weekIndex={this.props.weekIndex}
getChartReference={this.getChartReference}
//projectSpread={this.props.projectSpread}
selectedRows={this.props.selectedRows}
potentialProjectSpread={this.props.potentialProjectSpread}
selectedCompany={this.props.selectedCompany}
cascadeFilters={this.props.cascadeFilters}
selectedRows={this.props.selectedRows}
resized={this.props.resized}
selectedFilterData={this.selectedFilterData}
selectedProjects={this.selectedProjects}
data={this.data}
upperTarget={this.upperTarget}
lowerTarget={this.lowerTarget}
containsHistorical={this.containsHistorical}
addTargetLineGraph={this.addTargetLineGraph}
/>
</div>
);
}
};
function mapStateToProps(state){
let myValues = getChartValues(state);
return {
myStuff: myValues,
workloadData: state.chartData || new Immutable.List(),
weekIndex: state.weekIndex || null,
//projectSpread: state.projectSpread || {},
selectedRows: state.selectedRows || [],
potentialProjectSpread: state.potentialProjectSpread || [],
selectedCompany: state.companyFilter.selectedItems || null,
brokenOutByCompany: state.workloadGrid.brokenOutByCompany || false,
gridSortName: state.projectGridSort.name,
gridSortOrder: state.projectGridSort.order,
cascadeFilters: state.cascadeFilters || null,
selectedRows: state.selectedRows || [],
resized: state.chartResized || false,
selectedPotentialProjects: state.selectedPotentialProjects || []
};
}
module.exports = connect(mapStateToProps)(cntWorkloadChart);

Resources