In React, we can write components as pure functions. However, the problem with this is that you can't use it as stateful components because of the lack of lifecycle hooks and state. So, I wonder if is there any way to create stateful components without using classes.
Something that I found is the createClass helper. But, React has moved this helper into their own package in the release 15.5.0, link. Also, they recommend that you migrate them to JavaScript classes because classes are now the preferred way to create components in React. Therefore, I don't think that using this helper could be a good idea.
On the other hand, Facebook recommends the use of High Order Components (HOCs) which is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature. But, I couldn't find a way to create common stateful components without classes.
Has anyone gone through this? Is there any way to use React as a some purely functional solution?
React supports this since version 16.8. From the documentation:
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
A simple example:
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
For an example of how to use lifecycles, check out useEffect
Writing Stateful component without using classes is definitely a choice made by several developers. I recommend to use 'recompose' which has nice and easy implementation to write stateful components without class, yet apply state, both local and from store. Here is an example:
import compose from 'recompose/compose'
import withState from 'recompose/withState'
import withProps from 'recompose/withProps'
Pure.js
function MyComponent(props) ({
local: { prop1, prop2 },
setProp1
})
return <div>{prop1}</div>
}
const defaultState = {
prop1: false,
prop2: false
}
const enhance = compose(
withState('local', 'updateLocal', defaultState),
withProps(({ local: { prop1, prop2 }, updateLocal }) => ({
setProp1: (newValue) => updateLocal(state => ({...state, prop1: newValue }))
})))
export default enhance(MyComponent)
Maybe react-instance can become handy. Take a look at examples below.
Save state in local variable:
import React from "react"
import instance from "react-instance"
const App = instance(({ forceUpdate }) => {
let time = 0
const timer = setInterval(() => {
time++
forceUpdate()
}, 100)
return {
render() {
return time
},
unmount() {
clearInterval(timer)
},
}
})
Save state in component state:
import React from "react"
import instance from "react-instance"
const App = instance(instance => {
instance.state = { time: 0 }
const timer = setInterval(() => {
instance.setState({ time: instance.state.time + 1 })
}, 100)
return {
render() {
return instance.state.time
},
unmount() {
clearInterval(timer)
},
}
})
I tried to create a simple stateful component named Comp without the usage of es6 classes.
Here is the code
Basically I'm linking the prototype of the Comp function (our stateful component) to the prototype object of React.Component and I pass down to it Comp's props to initialize it properly. After that you can use every function of the React.Component object on the Comp.prototype. I used some just an example. I don't know if this is the best way in the "most javascript" way to use react
Related
I'm relatively new to React and this is what I'm trying to accomplish:
User selects an item from the sidebar.
The elementID is lifted up to the parent(app.js).
app.js sends it to its child, Graphs.
Graphs will create a Graph component and append to its graph array.
Is there a better way than this? P.S I'll have more than 1x useEffect in this component.
App.js
- Sidebar
- Title-bar
- Graphs
function Graphs(props) {
const [graphs, addGraphs] = useState([]);
useEffect(() => {
if (props.graphID) {
addGraphs(graphs.concat(props.graphID));
}
}, [props.graphID]);
return (
<div>{graphs}</div>
);
}
Thank you!
I believe it is a good approach, but you should use an functional state update. The "setter" functions of React.useState hook has a callback with previous state, so you shall update it like this:
import React from "react";
function MyComponent({id}) {
const [list, setList] = React.useState([]);
React.useEffect(() => {
if (id) {
setList(previousState => {
return [
...previousState,
id
];
});
}
}, [id]);
return (
<div>
{
list.map((_id, index) => {
<p key={index}>
{_id.toString()}
</p>
)
}
</div>
);
}
There's nothing wrong with sending props as you have done Jonathan.
If you are looking for other state management options you can look into the native React useContext, useReducer hooks to create your own system. Otherwise there are frameworks like Redux Saga, and more recently Recoil is worth checking out.
If you just simply want to select an ID and send it to another component, using props or useContext is probably your best bet.
I want to convert a PureComponent to a memoized FunctionalComponent, so it only re-renders if the props change, even if the parent re-renders.
export class MyComp extends React.PureComponent<{param: string}> {
public render() {
return <div>{this.props.param}</div>;
}
}
I want to change it so it's a functional component in order to use React Hooks.
export const MyComp: React.FC<{ param: string }> = useMemo(({param}) => {
return <div>{param}</div>;
}, [param]);
But the above doesn't work and there are several problems:
The destructed param is type is any and not correctly inferred.
I can not pass [param] as the dependencies list for useMemo because it was not defined in this context.
There seems to be no way to set the type of the parameters in the dependencies list. Is this because the parameters are just variables from the parent scope and not actual arguments that are passed in? If yes, how can we export a pure component if we don't know what props will be passed in?
Does it make more sense to have something like this?
export const MyComp: React.FC<{ param: string }> = (param) => {
return useMemo((param) => {
return <div>{param}</div>;
}, [param]);
};
Is this component memoized correctly? What if we also have some internal state our data from store, will it re-render when those change?
export const MyComp: React.FC<{ param: string }> = (param) => {
return useMemo((param) => {
// Will it re-render when this changes even if it's memoized?
const fromStore = useSelector((state: IState) => state.myValue));
return <div>{param} {fromStore}</div>;
}, [param]);
};
I don't think it will rerender if the store value changes. But in that case we would have to hoist fromStore outside useMemo, but doesn't this mean that the component is not pure anymore? As whenever the parent re-renders the MyComp function will run again (eg. compute fromStore value again).
I do like working with hooks, but their functionality and implementation is a bit abstract. What's the correct way of implementing a typed pure component with hooks?
You are using the wrong method here, React.memo is the equivalent of React.PureComponent.
React.useMemo is used to memoize expensive computations inside a function component.
import React, { memo } from 'react'
type Props = {
param: string
}
export const MyComp = memo(({ param }: Props) => (
<div>{param}</div>
))
Also, many people prefer to not type components with React.FC, you can read why here
When hiddenLogo changes value, the component is re-rendered. I want this component to never re-render, even if its props change. With a class component I could do this by implementing sCU like so:
shouldComponentUpdate() {
return false;
}
But is there a way to do with with React hooks/React memo?
Here's what my component looks like:
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import ConnectedSpringLogo from '../../containers/ConnectedSpringLogo';
import { Wrapper, InnerWrapper } from './styles';
import TitleBar from '../../components/TitleBar';
const propTypes = {
showLogo: PropTypes.func.isRequired,
hideLogo: PropTypes.func.isRequired,
hiddenLogo: PropTypes.bool.isRequired
};
const Splash = ({ showLogo, hideLogo, hiddenLogo }) => {
useEffect(() => {
if (hiddenLogo) {
console.log('Logo has been hidden');
}
else {
showLogo();
setTimeout(() => {
hideLogo();
}, 5000);
}
}, [hiddenLogo]);
return (
<Wrapper>
<TitleBar />
<InnerWrapper>
<ConnectedSpringLogo size="100" />
</InnerWrapper>
</Wrapper>
);
};
Splash.propTypes = propTypes;
export default Splash;
As G.aziz said, React.memo functions similarly to pure component. However, you can also adjust its behavior by passing it a function which defines what counts as equal. Basically, this function is shouldComponentUpdate, except you return true if you want it to not render.
const areEqual = (prevProps, nextProps) => true;
const MyComponent = React.memo(props => {
return /*whatever jsx you like */
}, areEqual);
React.memo is same thing as React.PureComponent
You can use it when you don't want to update a component that you think is static so, Same thing as PureCompoment.
For class Components:
class MyComponents extends React.PureCompoment {}
For function Components:
const Mycomponents = React.memo(props => {
return <div> No updates on this component when rendering </div>;
});
So it's just creating a component with React.memo
To verify that your component doesn't render you can just
activate HightlightUpdates in react extension and check your components reaction on
rendering
We can use memo for prevent render in function components for optimization goal only. According React document:
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
According to react documentation:- [https://reactjs.org/docs/react-api.html][1]
React. memo is a higher order component. If your component renders the
same result given the same props, you can wrap it in a call to React.
memo for a performance boost in some cases by memoizing the result.
This means that React will skip rendering the component, and reuse the
last rendered result.
For practical understanding I came across these two videos they are very good if you wanna clear concepts also, better to watch so it'll save your time.
Disclaimer:- This is not my YouTube channel.
https://youtu.be/qySZIzZvZOY [ useMemo hook]
https://youtu.be/7TaBhrnPH78 [class based component]
Im new to react, i'm trying to implement/learn stateless component, Im having difficulty in using component will mount in stateless component.
My code
const Terms = (actions, commonReducer) => {
componentDidMount() {
actions.userActions()
}
return (
<div className="jobUpdate">
<form onSubmit={(e) => {
e.preventDefault(); actions.userInput(document.getElementById('enteredVal').value)
}}>
<input type="text" id="enteredVal" />
<button type="submit"></button>
</form>
</div>
);
};
I know the stateless component does not have life cycle hooks, but wanted alternate approach to preform component did mount in stateless component.
Any help with this is much appreciated. Thanks in advance
You could always wrap the component in another component, using a pattern known as higher-order components.
A higher-order component (HOC) is a function that takes a component and returns a new component.
Perhaps the most widely used example of this technique is react-redux, which uses the connect() method to create components connected to the redux store.
Instead of creating your own HOC, there are also libraries out there that can do it for you, such as react-redux-lifecycle.
However, what you are attempting with this component is not a very common pattern -- it is much more common to instead keep the handling of business and data in a container component, and leave presentational components to inherit store actions and data from props. Check out Dan Abramov's Presentational and Container Components for a very good overview on how and why to break down components into these two categories!
Starting in React 16.8, you can accomplish the same kind of functionality using a useEffect hook.
In your specific example, we'd have something like this:
import React, { useEffect } from 'react';
// other imports and declarations
function Example() {
// Similar to componentDidMount
useEffect(() => {
// This function will be run on component mount
myAction();
}, []); // The second argument of [] tells react to only perform the effect on mount
return (
<div>
... your component
</div>
);
}
export default Example;
The docs do a great job of explaining this, and I'd encourage you to read up on it. Keep in mind that it's not exactly the same thing going on behind the scenes, and so the patterns will not be a one-to-one correspondence; but these patterns should help with the majority of your cases.
Just know the following basic ideas:
The first argument of a useEffect hook is a "side effect" function. It is always run after the first component's render, and then conditionally afterwards.
This "side effect" function can return a "cleanup" function. The "cleanup" function is run just before the next time the "side effect" function is run. It will always be run before unmounting the component.
The second, optional, argument of a useEffect hook is an array of dependencies.
If any value in the dependency changes, the "side effect" function will be run after the next render.
Anyway, in the meantime, here's a few patterns to emulate class component behavior.
componentDidMount + componentDidUpdate
useEffect(() => {
console.log("This line will be run after each render ");
});
componentDidUpdate when a given value changes
// get myValue from component props
const { myValue } = props;
useEffect(() => {
console.log("This line will be run after each render where myValue changed");
}, [myValue]);
componentDidUpdate when a given value changes, pt. 2
// get myValue from component props
const { myValue } = props;
const myCondition = myValue === "yes";
useEffect(() => {
console.log('This line will be run after each render where the returned value of the statement `myValue === "yes"` changes from false to true or true to false ');
}, [myCondition]);
componentDidUpdate when a given value changes, pt. 3
// get myValue from component props
const { myValue, myValue2 } = props;
useEffect(() => {
console.log("This line will be run after each render where myValue OR myValue2 changed");
}, [myValue, myValue2]);
componentDidMount
useEffect(() => {
console.log("This line will be run only after the first render ");
}, []);
componentWillUnmount
useEffect(() => {
// nothing will be run as a side effect...
return () => {
// but this will be run as clean up
console.log("This line will be run just before the component unmounts");
};
}, []);
componentDidUpdate (without componentDidMount)
import React, {useEffect, useRef} from 'react';
export default function MyComponent() {
// create a reference value which does not trigger a re-render when changed
const isMounted = useRef(false);
useEffect(() => {
if( isMounted.current === false ){
// on mount, set the ref to true
isMounted.current = true;
} else {
// the component is already mounted
console.log("This line will be run after each render except the first ");
}
});
return (<div />);
}
Hope this will be useful to someone.
Throw a dumb work around using HOC (high order component)
const withLifecycles = (MyStatelessComp) => class extends React.PureComponent {
static propTypes = {}
static displayName = "withPure(xxx)"
state = {}
componentDidMount() {}
render() {
return <MyStatelessComp {..this.state, ...this.props} />
}
}
then use it as
MyStatelessWithLifecycles = withLifecycles(props => {
...
return <Bla />
}
Though not sure what's the reason for a stateless component to have the lifecycles there, it's meant to be pure and simple (presentation only).
I want to make reusable modules that could be plugged in to any react-redux application. Ideally, my module would have a container component, actions, and reducer at the top level (and then any presentational components below the container). I would want the module to only work off its own slice of the app's state, and ideally to not have to know anything about the rest of the app state (so it's truly modular).
Reducers only work off of part of the state (using combineReducers), so I'm happy there. However, with container components, it seems like mapStateToProps always takes in the full state of the app.
I'd like it if mapStateToProps only took in the same "state slice" that I am handling in my module (like the reducer does). That way my module would truly be modular. Is this possible? I guess I could just pass that slice of the state down to be the props of this component (so I could just use the second argument of mapStateToProps, ownProps), but am not sure if this would have the same effect.
That is actually something of a complicated topic. Because Redux is a single global store, the idea of a completely encapsulated, fully reusable plug-and-play set of logic does become rather difficult. In particular, while the reducer logic can be fairly generic and ignorant of where it lives, the selector functions need to know where in the tree to find that data.
The specific answer to your question is "no, mapState is always given the complete state tree".
I do have links to a number of relevant resources, which may possibly help with your situation:
There's several existing libraries that try to implement "per-component state in Redux". I have a list of them in my Redux addons catalog, in the Component State category.
A group of devs have been discussing and prototyping various approaches to the "reusable logic module in Redux" concept. Their work is at https://github.com/slorber/scalable-frontend-with-elm-or-redux .
Randy Coulman recently posted a three-part blog series related to state encapsulation and modularity in Redux. He didn't come up with definitive answers, but the posts are worth reading: Encapsulating the Redux State Tree, Redux Reducer Asymmetry, and Modular Reducers and Selectors.
Although mapStateToProps (the first function you pass to connect) gets passed the whole store as you said, its job is to map specific parts of the state to the component. So only what is returned from mapStateToProps will be mapped as a prop to your component.
So lets say your state looks like this:
{
account: {
username: "Jane Doe",
email: "janedoe#somemail.com",
password: "12345",
....
},
someOtherStuff: {
foo: 'bar',
foo2: 'bar2'
},
yetMoreStuff: {
usuless: true,
notNeeded: true
}
}
and your component needs everything from account and foo from someOtherStuff then your mapStateToProps would look like this:
const mapStateToProps = ({ account, someOtherStuff }) => ({
account,
foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)
then your component will have the prop account and foo mapped from your redux state.
Redux only has a single store as you know, so all it knows to do is pass the entire store to your mapStateToProps function. However using object destructuring, you can specify which properties in the store you want and ignore the rest. Something like 'function mapStateToProps({prop1, prop2})' would only capture those two properties in the store and ignore the rest. Your function is still receiving the entire store, but you're indicating that only these props interest you.
In my example, 'prop1' and 'prop2' would be the names you assigned your reducers during the call to 'combineReducers'.
Ideally the way it works is you get the state and you extract the values from them by use deconstructors. redux works on concept of single state
For example:-
function mapStateToProps(state){
const { auth } = state //just taking a auth as example.
return{
auth
}
}
I'm running into the same problem because, as you said, the current implementation of redux/react-redux allows for splitting up reducers on the state just fine but mapDispatchToProps always passes the whole state tree.
https://stackoverflow.com/a/39757853/444794 is not what I want, because it means we have to duplicate all our selector logic across each react-redux application that uses our module.
My current workaround has been to pass the slice of the state down as a prop instead. This follows a sort of compositional pattern but at the same time removes the cleanliness of accessing the state directly, which I'm disappointed with.
Example:
Generally, you want to do this:
const mapStateToProps = (state) => {
return {
items: mySelector(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
but since this module is included in an application where the state is now several things (ie. key-values) rather than a list of items, this won't work. My workaround instead looks like:
const mapStateToProps = (_, ownProps) => {
return {
items: mySelector(ownProps.items)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
And the application using the Module looks like:
const mapStateToProps = (state) => {
return {
items: state.items
stuffForAnotherModule: state.otherStuff
}
}
class Application extends React.Component {
render() {
return (
<div>
<ModularComponent items={ this.props.items } />
<OtherComponent stuff={ this.props.stuffForAnotherModule } />
</div>
)
}
}
export default connect(mapStateToProps)(Application)
You do have the option of writing a couple of wrapper utils for your modules that will do the work of: 1) Only running mapStateToProps when the module's slice of state changes and 2) only passes in the module's slice into mapStateToProps.
This all assumes your module slices of state are root properties on the app state object (e.g. state.module1, state.module2).
Custom areStatesEqual wrapper function that ensures mapStateToProps will only run if the module's sub-state changes:
function areSubstatesEqual(substateName) {
return function areSubstatesEqual(next, prev) {
return next[substateName] === prev[substateName];
};
}
Then pass it into connect:
connect(mapStateToProps, mapConnectToProps, null, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
Custom mapStateToProps wrapper that only passes in the module substate:
function mapSubstateToProps(substateName, mapStateToProps) {
var numArgs = mapStateToProps.length;
if (numArgs !== 1) {
return function(state, ownProps) {
return mapStateToProps(state[substateName], ownProps);
};
}
return function(state) {
return mapStateToProps(state[substateName]);
};
}
And you'd use it like so:
function myComponentMapStateToProps(state) {
// Transform state
return props;
}
var mapSubstate = mapSubstateToProps('myModuleSubstateName', myComponentMapStateToProps);
connect(mapSubstate, mapDispatchToState, null, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
While untested, that last example should only run myComponentMapStateToProps when 'myModuleSubstateName' state changes, and it will only receive the module substate.
One additional enhancement could be to write your own module-based connect function that takes one additional moduleName param:
function moduleConnect(moduleName, mapStateToProps, mapDispatchToProps, mergeProps, options) {
var _mapState = mapSubstateToProps(moduleName, mapStateToProps);
var _options = Object.assign({}, options, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
});
return connect(_mapState, mapDispatchToProps, mergeProps, _options);
}
Then each module component would just need to do:
moduleConnect('myModuleName', myMapStateToProps)(MyModuleComponent);
The answer to your question is yes. Both given answers cover different aspects of the same thing. First, Redux creates a single store with multiple reducers. So you'll want to combine them like so:
export default combineReducers({
people: peopleReducer,
departments: departmentsReducer,
auth: authenticationReducer
});
Then, say you have a DepartmentsList component, you may just need to map the departments from the store to your component (and maybe some actions mapped to props as well):
function mapStateToProps(state) {
return { departments: state.departments.departmentsList };
}
export default connect(mapStateToProps, { fetchDepartments: fetchDepartments })(DepartmentsListComponent);
Then inside your component it is basically:
this.props.departments
this.props.fetchDepartments()