useSelector with React.memo vs connect - redux

referring from the link.
https://react-redux.js.org/next/api/hooks#performance
what i understand the benefit of useSelector hook, is to avoid wrapper hell. Wrapper hell is happening due to the usage of connect HOC. If we have to use React.memo HOC with useSelector due to perfomance reason, would it be better approach to simply use connect HOC instead? Because in any case we would have to be in hell of wrappers. If the hell is not by connect then would be by React.memo.
Any one please explain the benefit of React.memo over connect.

Well, first, interesting enough although React.memo is a HOC it does not create the same nesting as connect does. I have created a test code:
import React from "react";
import ReactDOM from "react-dom";
import {connect, Provider} from 'react-redux'
import { createStore } from 'redux'
import "./styles.css";
const MemoComponent = React.memo(function MyMemo() {
return <div>Memo</div>;
});
const ConnectedComponent = connect(null,null)(function MyConnected() {
return <div>ReduxConnectComponent</div>;
})
const store = createStore(()=>{},{})
function App() {
return (
<Provider store={store}>
<MemoComponent />
<ConnectedComponent/>
</Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And here is the structure rendered:
We can see that a content for connect is rendered deeper.
Second, the docs say:
by default useSelector() will do a reference equality comparison of the selected value when running the selector function after an action is dispatched, and will only cause the component to re-render if the selected value changed. However, unlike connect(), useSelector() does not prevent the component from re-rendering due to its parent re-rendering, even if the component's props did not change.
that means the component which useSelector will not be re-rendered when unrelated parts of the store change. And this is the most important part of the optimization. Whether optimizing with React.memo or not is now completely depends on your decision and in most cases, it simply is not needed. We use React.memo only in cases when the component is very expensive to render.
To summarize, connect wrapper was required to connect to the store. With useSelector we do not have to wrap anymore. We still need to wrap with React.memo in rare cases when we need to optimize some heavy components. The work of React.memo was also done by connect but in most cases, it was premature optimization.

I have been trying to get an answer for quite some time but the answers I got weren't clear. Although the theory in the Redux documentation isn't complicated: useSelector uses strict equality === and connect uses shallow equality to determine. So in both cases, if you are "pulling" a primitive value from your Redux state (number, string, boolean) you will be having the same outcome. If values haven't changed none of the components will rerender. If you are "pulling" non-primitives (arrays or objects) and the values haven't changed for both cases (useSelector, connect), then the component that uses useSelector will still rerender as of course [] === [] will always be false, as they are referencing different arrays, where as the connected component will NOT rerender. Now in order to make useSelector behave similarly and not rerender, you can do this:
const object = useSelector(state => state.object, shallowEqual) You can import shallowEqual from react-redux. Or alternatively use a memoized version of that piece of state by using the reselect library:
const makeGetObject = () => createSelector(state => state.object, object => object)
and add it to your selector such as: const object = useSelector(state => state.object, makeGetObject); I have created this codesandbox when I was trying to get at the bottom of it (check the comments at the WithUseSelector component): useSelector vs connect()

I just customized useSelector hook to avoid that and it works nice
import { useSelector, useDispatch } from 'react-redux'
import { _lodash } from '../../../lodash'
export const useCloneSelector = (selector = (obj) => obj) => {
const selectWithClonedState = (state = {}, ...others) => selector(_lodash.cloneDeep(state), ...others)
return useSelector(selectWithClonedState, _lodash.isEqual)
}
export { useDispatch, useSelector }

Related

Setting up React Testing library with NextJS, GraphQL, Apollo and i18n - Cannot destructure property

As the title suggests I'm having difficulty setting up React Testing Library on a project that uses NextJS, GraphQL, Apollo and i18n
Terminal was complaining about the router not being set up so I used next-router-mock to mock it as explained in their documentation and now I'm getting this error which I can't wrap my head around
TypeError: Cannot destructure property 'pageTitle' of '_languageContent.default[locale]' as it is undefined.
// __tests__/index.test.jsx
import { render, screen } from '#testing-library/react'
import Home from '../src/pages/index'
import '#testing-library/jest-dom'
import { QueryClient, QueryClientProvider } from "react-query"
import mockRouter from 'next-router-mock';
jest.mock('next/router', () => require('next-router-mock'));
const queryClient = new QueryClient()
describe('Home', () => {
it('renders a heading', () => {
mockRouter.push("/en-US");
render(
<QueryClientProvider client={queryClient} >
<Home />
</QueryClientProvider>
)
const heading = screen.getByText('stuff');
expect(heading).toBeInTheDocument()
})
})
My assumption is that my test is not receiving the language strings it requires - the language strings are determined by the URL - http://localhost:3000/en-US for English http://localhost:3000/fr-FR for French.
On the page component itself, a languageContent const is fed a variable from useRouter to determine which strings to use
But I have tried to set the URL with mockRouter but this did nothing
Here is the component I am trying to test

How i can Remove Data from redux automatic using setTimeOut

Hiii i have one array like more than one object and when store data from redux i need to remove automatic from array after 5 second.
welcome to the community! For your problem maybe use a useEffect hook in the app component, eg:
import foo from "foo.js";
import blahblahblah from "xxx.js";
import React from "react";
import {useDispatch} from "react-redux";
// ..;
function App(){
const dispatch = useDispatch();
React.useEffect(()=>{
while(true){
setTimeout(()=>{
dispatch.runSomthing(myAmazingData);
},5000);
}
}, []);
return(
<div>
<p>Welcome to my amazing website😀</p>
</div>)
}
export default App;
Next time, show some code! And explain your intentions and what you want to do.
Thanks!

Meteor and withTracker: why is a component rendered twice?

I have created a bare-bones Meteor app, using React. It uses the three files shown below (and no others) in a folder called client. In the Console, the App prints out:
withTracker
rendering
withTracker
rendering
props {} {}
state null null
In other words, the App component is rendered twice. The last two lines of output indicate that neither this.props nor this.state changed between renders.
index.html
<body>
<div id="react-target"></div>
</body>
main.jsx
import React from 'react'
import { render } from 'react-dom'
import App from './App.jsx'
Meteor.startup(() => {
render(<App/>, document.getElementById('react-target'));
})
App.jsx
import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
class App extends React.Component {
render() {
console.log("rendering")
return "Rendered"
}
componentDidUpdate(prevProps, prevState) {
console.log("props", prevProps, this.props)
console.log("state", prevState, this.state)
}
}
export default withTracker(() => {
console.log("withTracker")
})(App)
If I change App.jsx to the following (removing the withTracker wrapper), then the App prints only rendering to the Console, and it only does this once.
import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
export default class App extends React.Component {
render() {
console.log("rendering")
return "Rendered"
}
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, this.props)
console.log(prevState, this.state)
}
}
What is withTracker doing that triggers this second render? Since I cannot prevent it from occurring, can I be sure that any component that uses withTracker will always render twice?
Context: In my real project, I use withTracker to read data from a MongoDB collection, but I want my component to reveal that data only after a props change triggers the component to rerender. I thought that it would be enough to set a flag after the first render, but it seems that I need to do something more complex.
This a "feature", and it's not restricted to Meteor. It's a feature of asynchronous javascript. Data coming from the database arrives after a delay, no matter how quick your server is.
Your page will render immediately, and then again when the data arrives. Your code needs to allow for that.
One way to achieve this is to use an intermediate component (which can display "Loading" until the data arrives). Let's say that you have a component called List, which is going to display your data from a mongo collection called MyThings
const Loading = (props) => {
if (props.loading) return <div>Loading...</div>
return <List {...props}></List>
}
export default withTracker((props) => {
const subsHandle = Meteor.subscribe('all.myThings')
return {
items: MyThings.find({}).fetch(),
loading: !subsHandle.ready(),
}
})(Loading)
It also means that your List component will only ever be rendered with data, so it can use the props for the initial state, and you can set the PropTypes to be isRequired
I hope that helps
Unsure if you're running into the same error I discovered, or if this is just standard React behavior that you're coming into here as suggested by other answers, but:
When running an older (0.2.x) version of react-meteor-data on the 2.0 Meteor, I was seeing two sets of distinct renders, one of which was missing crucial props and causing issues with server publications due to the missing data. Consider the following:
// ./main.js
const withSomethingCount = (C) => (props) => <C { ...props } count={ ... } />
const withPagination = (C) => (props) => <C { ...props } pagination={ ... } />
const withSomething = withTracker((props) => {
console.log('withSomething:', props);
});
// Assume we're rending a "Hello, World" component here.
export const SomeComponent = withSomethingCount(withPagination(withSomething(...)));
// Console
withSomething: { count: 0 }
withSomething: { count: 0, pagination: { ... } }
withSomething: { count: 0 }
withSomething: { count: 0, pagination: { ... } }
For whatever reason, I was seeing not only N render calls but I was seeing N render calls that were missing properties in a duplicate manner. For those reading this and wonder, there was one and only one use of the component, one and only one use of the withTracker HoC, the parent HoCs had no logic that would cause conditional passing of props.
Unfortunately, I have not discovered a root-cause of the bug. However, creating a fresh Meteor application and moving the code over was the only solution which removed the bug. An in-place update of the Meteor application (2.0 to 2.1) and dependencies DID NOT solve the issue... however a fresh installation and running a git mv client imports server did solve my problems.
I've regrettably had to chalk this up to some form of drift due to subsequent Meteor updates over the two years of development.

Contextual styling of ReactJS styled-components

I'm running into problems with Styled Components, and I'm not sure if it's a technical limitation, or if my mental model of how to use them is wrong.
In this example (I just wrote it quickly in Codepen, so disregard spelling mistakes) I have a component that is a username in a span. I want to be able to use that component anywhere, so it's styling is pretty minimal:
// From User.js
import React from 'react';
import styled from 'styled-components';
const Username = styled.span`
color: black;
`
const User = () => (
<UserName>bla</Username>
);
export default User;
I want to be able to use this span in a different component, but in that case I want it's styling to be overridden in this context:
// From Userblock.js
import React from 'react';
import styled from 'styled-components';
import User from './user';
const UserWrapper = styled.div`
// Some styles
`
const User = styled(User)`
color: red; // Different color when displayed in a UserBlock
`
const UserBlock = () => (
<UserWrapper>
<User />
</UserWrapper>
);
export default UserBlock;
From what I know, that's the way to set styling on a third party component (and that works elsewhere in my application), and it should work as far as I can tell, but I get nothing.
Also, going back to the mental model question - is this the way you're supposed to do it when you're using styled-components?
Thanks for any help!
When you wrap a normal React component with styled(X) you need to make sure to attach the class that styled-components passes in.
This means for your example, this will solve the issue:
const User = (props) => (
<UserName className={props.className}>bla</Username>
);
When you override the styles of an existing component styled-components goes ahead, generates a class, injects it and passes it to the component. That means if you don't attach the class to anything you'll never see the styles though! (see the documentation for more information)

How to load data with Meteor and React?

I'm using Meteor 1.3.5 and React 15.1.0 and trying to understand the right way to load and subscribe to data from MongoDB.
Right now I'm doing this in createContainer with params, and having problems waiting for the data to be available.
Should I use states instead of props, and load the data in componentWillMount or componentWillMount? These didn't worked for me so far. I also tried to work with getMeteorData, but it isn't doing anything when the component renders.
I'm looking for a solution that will update the component when new data is coming. Thanks
import React, {Component, PropTypes} from "react";
import ReactDOM from "react-dom";
import { createContainer } from "meteor/react-meteor-data";
export default class UsersComponent extends Component{
render(){
let users = this.props.users;
console.log(users);
return (
<div>
{
(users)?
(users.map((user, i) => (
<div key={user._id}>
<p>{user.name}</p>
</div>
)))
: null
}
</div>
)
}
}
UsersComponent.propTypes = {
users: PropTypes.array.isRequired,
}
export default createContainer(({ params }) => {
return {
users: Meteor.users.find().fetch(),
};
}, UsersComponent);
Avoid using React's state to manage data with Meteor. Instead, create stateless functional components that uses only props. Read these:
Functional Components vs. Stateless Functional Components vs. Stateless Components
Stateless Functional Components in React 0.14
This makes your UI component easily reusable, regardless of how you want to laod data.
To understand how to load (reactive) data, it is useful to understand the concept/difference between presentational and container components.
Next step is to create container components using a technique of your choice that wraps/renders a UI component. Meteor guide's createContainer is the de facto approach for now. There are also other options such as Mantra (some say better but more complex).

Resources