I have a redux connected component that is utilising areStatesEqual in the options argument in the redux connect api to avoid re-rendering.
shouldComponentUpdate does the same job, but a lot slower in my use case.
I'm struggling to test multiple state updates, asserting that under certain conditions this component believes the states are equal, and shouldn't re-render.
I'm using react-mock-store for other components, but that only deals with static state, not dynamic. Also using mocha & enzyme elsewhere too.
Does anyone have any ideas how I can test this behaviour?
(This is a simplified version of a more complicated component)
import { connect } from 'react-redux';
import { compose } from 'redux';
import MyComponent from './my-component';
const mapStateToProps = (state, props) => ({
text: state.a.text[props.id],
});
const areStatesEqual = (next, prev) => {
return next.a === prev.a;
};
const options = { areStatesEqual };
export default connect(mapStateToProps, undefined, undefined, options)(MyComponent);
MyComponent is a simple functional component that just accepts props and renders:
export default function Label({ text }) {
return <p>{text}</p>;
}
In the end I managed to test the logic by exporting the areStatesEqual function and doing a simple test.
export const areStatesEqual = (next, prev) => {
return next.a === prev.a;
};
I haven't figured out a way to test the flow of the lifecycle of a component
Related
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]
I cannot figure out what going on with "useSelector" I need little help, please.
ERROR
React Hook "useSelector" is called in function "render_user" which is neither a React function component or a custom React Hook function
class Navigationbar extends Component {
onLogoutClick = e => {
e.preventDefault();
this.props.logoutUser(); //this.props.
};
render() {
const render_user = () => {
const auth = useSelector(state => state.auth); Error Message is here
//More Code Here
);
};
}
Navigationbar.propTypes = {
logoutUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(
mapStateToProps,
{ logoutUser }
)(Navigationbar);
The error is due to the fact that you violated the rules of hooks:
Only Call Hooks at the Top Level
Only Call Hooks from React Functions
Violations:
useSelector() is not called in top-level. It is called in render_user() in render() (i.e nested function).
useSelector() is part of a class component, Navigationbar
You can extract a component to follow the rules of hooks and make use of useSelector:
function User() { // Rule 2: call hooks in function component
const auth = useSelector(state => state.auth); // Rule 1: call hooks in top-level
return <>{auth}</>
}
class Navigationbar extends Component {
render_user() {
if (props.authenticated) {
return <User />
}
// not yet authenticated
// do something else
}
render() {
return <>{this.render_user()}</>
}
}
If you are using TypeScript and (obviously) a functional component. Make sure your component name begins with a capital letter and not a lowercase letter.
useSelector is new hook that is added by react-redux after the addition of new Hooks API to react.
These hooks can only be used in function component
Following the React v6 upgrade, my existing test cases for the component are failing.
Here is my component container code TodoContainer.jsx:
import { connect } from 'react-redux';
import Todo from './Todo';
import { initialLoadExecuted } from '../../actions/LoadActions';
const mapStateToProps = state => ({
isCollapsed: true,
pinnedTiles: false,
});
const mapDispatchToProps = dispatch => ({
dispatchInitialLoadExecuted: (tiles) => {
dispatch(initialLoadExecuted(tiles));
},
});
export default connect(mapStateToProps, mapDispatchToProps)(Todo);
Here is my test code TodoContainer.test.jsx:
import React from 'react';
import configureStore from 'redux-mock-store';
import {Provider} from 'react-redux';
import TodoContainer from '../../../../src/todo-component/components/Todo/TodoContainer';
import { initialLoadExecuted } from '../../../../src/todo-component/actions/LoadActions';
const mockStore = configureStore();
let store;
describe('Todo Container', () => {
beforeEach(() => {
store = mockStore({
});
it('maps state props correctly', () => {
const wrapper = shallow(<TodoContainer store={store}/>);
wrapper.prop('dispatchInitialLoadExecuted')('Test String);
// Expect statements
});
});
The error i am getting is :
Invariant Violation: Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: . You may also pass a {context : MyContext} option to connect.
Is there a way to to pass store through provider while accessing the props, the same way?
It appears that react-redux v6.0.0 now supports the new Context API added to React v 16.4.0 (and also requires that verson of react now).
I was able to resolve the issue and keep the mockStore pattern by installing react-redux#5.1.1 and react#16.3.0 (before they introduced the Context API).
Further testing: I can use react#16.7.0 as long as I use react-redux#5.1.1
There's an ongoing discussion on the react-redux github issues tab: https://github.com/reduxjs/react-redux/issues/1161
Not a long term solution as I'd be stuck at this version of React, but it does pass the test and I was able to get my 100% code coverage.
I'm trying to figure out how to user the reducers with and inside my React-Component.
My goal is pretty easy - at least i thought so: I want to toggle a Drawer-Menu. I know I can solve this with React-Only, but I want to learn Redux.
So, I've got a Component…
import React, { Component } from 'react';
class Example extends Component {
// ???
render() {
return (
<button className="burgerbutton" onClick={this.toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
);
}
}
export default Example;
also a Reducer
const initialState = {
buttonstate: false
};
const example = (state = initialState, action) => {
switch (action.type) {
case 'TOGGLE_BTN':
return Object.assign({}, state, {
buttonstate: !state.buttonstate
})
default:
return state
}
}
export default example
and an Action (although I don't know where to put that since it's so simple)
export const toggleDrawer = () => {
return {
type: 'TOGGLE_DRAWER'
}
}
I read a lot of tutorials and most of them want me to seperate between "Presentational Components" and "Container Components". I can't really see how these concepts apply here.
So what do I have to do to do to make this work? Am I looking at this problem from the right angle or do I need 12 "Container Components" to solve this?
I really hope this question makes sense at all and/or is not a duplicate!
In redux you have to dispatch action to update reducer state. So normally a component is connected to the redux store and communication is done through dispatch.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { toggleDrawer } from 'action file location';
class Example extends Component {
toggleDrawerHandler() {
this.props.dispatch(toggleDrawer())
}
render() {
// access button state from this.props.buttonstate
return (
<button className="burgerbutton" onClick={this.toggleDrawerHandler.bind(this)}</button>
<div className="drawerMenu isvisible" ></div>
);
}
}
export default connect((store) => {buttonstate: store.buttonstate})(Example);
First, I'm really enjoying using redux "ducks" which is basically a redux reducer bundle. You put your reducer, action constants, and action creators in one file (called a duck). Then you may have multiple ducks for different modules or pieces of state that you'd then combine with combineReducers.
While #duwalanise has the right idea, I'd rather see the second param of connect() be used to directly map the action to dispatch (and there's a good shortcut for it) instead of having to use this.props.dispatch
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { toggleDrawer } from './duck';
class Example extends Component {
render() {
const { buttonstate, togglerDrawer } = this.props;
return (
<div>
<button className="burgerbutton" onClick={toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
</div>
);
}
}
const mapStateToProps = (state) => ({
buttonstate: state.buttonstate,
});
export default connect(mapStateToProps, { toggleDrawer })(Example);
One side note, if you have a handler method in your component, it's better to do .bind(this) inside the constructor instead of using an arrow function or .bind(this) inside the event, ie don't do this onClick={() => /* do something */ } or this onClick={this.myHandler.bind(this)} This is an interesting (and long) read on it.
To touch on the Container vs Presentational Component piece: The idea would be to put all of your logic, handlers, redux actions etc into your containers, and pass that through props to your simple (hopefully stateless/pure function) presentational components. Technically, your component the way it's written could be turned into a stateless component:
const Example = ({ buttonstate, togglerDrawer }) => (
<div>
<button className="burgerbutton" onClick={toggleDrawer}</button>
<div className="drawerMenu isvisible" ></div>
</div>
);
I have a "utility" called "reduxify" that automatically does a lot of the Redux/React boilerplate for you, so that instead of writing "mapStateToProps" and "mapDispatchToProps" functions on every component, you just write your component like this:
// usage.
class Foo extends Component {
// component stuff
}
export default reduxify(actions, Foo);
The reduxify function (with comments) are at this gist:
https://gist.github.com/brianboyko/904d87da2a75c98e8cd5f5352dd69d57
Without the comments (for brevity), it's produced below:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { getStore } from '../store/storeConfig'
export function reduxify(actions, component){
let mapStateToProps = (state) => {
state = Object.assign({}, state, {store: state}, {getStore: getStore});
return (state);
}
let prepareActions = (actions) => (dispatch) =>
({ actions: bindActionCreators(actions.default, dispatch),
dispatch: dispatch,
})
let mapDispatchToProps = (dispatch) => (prepareActions(actions, dispatch))
return connect(mapStateToProps, mapDispatchToProps)(component);
}
So, here's the question: What's the best way to mock a component (one that will have access to the Provider) so that I can unit test this sucker, put it out there for people to use and enjoy, and not feel like a hack?
Have a look at the unit tests used by react-redux for an example of how to test a component that is wrapped by <Provider>: https://github.com/reactjs/react-redux/blob/master/test/components/Provider.spec.js#L50-64. Basically, they just declared a component and then wrapped it with a <Provider> component whose store prop is a mocked Redux store. (They're using 'react-addons-test-utils' to do their test component rendering, but it would be even easier with enzyme.)
In your case, you could spy on the mocked store's dispatch() method to ensure that your component's action-dispatching props are calling it as expected.