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
Related
When I used the same method in another project then It was working well but I decided to use the same method for my current project then I am having an issue with the given below
react-dom.development.js:14724 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See some tips for tips about how to debug and fix this problem.
at Object.throwInvalidHookError (react-dom.development.js:14724:13)
at useState (react.development.js:1497:21)
Below is my first component name is GetWindowWidth.js. this component is related to apply the screen with for desktop, tab, and mob screen
GetWindowWidth.js
import {useState,useEffect} from "react";
const GetWindowWidth = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
window.addEventListener("resize", updateWidth);
return () => window.removeEventListener("resize", updateWidth);
});
const updateWidth = () => {
setWidth(window.innerWidth);
};
return width;
}
export default GetWindowWidth;
Below is another component where I am trying to call the above component to apply the screen width.
AnotherComponent.js
import React, { Component } from 'react'
import GetWindowWidth from './GetWindowWidth';
export class AnotherComponent extends Component {
render() {
const width = GetWindowWidth();
return (
<div color={width<600? '#161625':"#f3990f"}>UserCatalogTwo</div>
)
}
}
export default AnotherComponent
I don't why this is coming even it's working on other projects.
GetWindowWidth is a hook, not a component, since it doesn't render anything. (And for that reason its name should start with use.) You can't use hooks in class components. You'll have to either rewrite the class component as a function component, or write a non-hook version of GetWindowWidth.
For instance, you might have a module with a function that sets up the resize handler:
// watchwidth.js
export function watchWidth(callback) {
const handler = () => {
callback(window.innerWidth);
};
window.addEventListener("resize", handler);
return () => {
window.removeEventListener("resize", handler);
};
}
...then import it:
import { watchWidth } from "./watchwidth.js";
...and use it in your class component:
componentDidMount() {
this.stopWatchingWidth = watchWidth(width => this.setState({width}));
}
componentWillUnmount() {
this.stopWatchingWidth();
}
That's off-the-cuff and untested, but gives you an idea.
I'm having serious issues with the "new" React Context ( https://reactjs.org/docs/context.html ) to work like I want/expect from the documentation. I'm using React v.16.8.6 (upgrading will probably take ages, it's a big app). I know there is a bit of a mix between old and new stuff but plz don't get stuck on that..
I did it like this to be as flexible as possible but it doesn't work.
The issue is, when it comes to contextAddToCart(..) it only executes the empty function instead of the one I defined in state as the documentation this.addToCart. I have consumers in other places as well. It seems like perhaps it's executing this in the wrong order. Or every time a Compontent imports MinicartContext it's reset to empty fn.. I don't know how to get around this..
I'll just post the relevant code I think will explain it best:
webpack.config.js:
const APP_DIR = path.resolve(__dirname, 'src/');
module.exports = function config(env, argv = {}) {
return {
resolve: {
extensions: ['.js', '.jsx'],
modules: [
path.resolve(__dirname, 'src/'),
'node_modules',
],
alias: {
contexts: path.resolve(__dirname, './src/contexts.js'),
},
contexts.js
import React from 'react';
export const MinicartContext = React.createContext({
addToCart: () => {},
getState: () => {},
});
MinicartContainer.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
MinicartContext,
} from 'contexts';
export default class MinicartContainer extends Component {
constructor(props) {
super(props);
this.addToCart = (product, qty) => {
const { prices } = product;
const { grandTotal, qtyTotal } = this.state;
this.setState({
grandTotal: grandTotal + prices.price,
qtyTotal: qtyTotal + qty,
});
};
this.state = {
grandTotal: -1,
qtyTotal: -1,
currencyCode: '',
addToCart: this.addToCart,
};
}
render() {
const { children } = this.props;
return (
<MinicartContext.Provider value={this.state}>
{children}
</MinicartContext.Provider>
);
}
Header.jsx:
import React, { Component } from 'react';
import {
MinicartContext,
} from 'contexts';
class Header extends Component {
render() {
return (
<div>
<MinicartContainer MinicartContext={MinicartContext}>
<Minicart MinicartContext={MinicartContext} />
</MinicartContainer MinicartContext={MinicartContext}>
{/* stuff */}
<MinicartContainer MinicartContext={MinicartContext}>
<Minicart MinicartContext={MinicartContext} />
</MinicartContainer MinicartContext={MinicartContext}>
</div>
)
}
}
export default Header;
AddToCartButton.jsx
import {
MinicartContext,
} from 'contexts';
export default class AddToCartButton extends Component {
addToCart(e, contextAddToCart) {
e.preventDefault();
const QTY = 1;
const { product, active } = this.props;
// doing stuff ...
contextAddToCart(product, QTY);
}
render() {
return (
<React.Fragment>
<MinicartContext.Consumer>
{({context, addToCart}) => (
<div
onClick={(e) => { this.addToCart(e, addToCart); }}
Seems to me that you don't have fully understand how the context API words.
Here's my HOC implementation of contexts, maybe it can help you to understand better how things work.
export const MinicartContext = React.createContext({}) // Export the Context so we can use the Consumer in class and functional components (above). Don't use the Provider from here.
// Wrap the provider to add some custom values.
export const MinicartProvider = props => {
const addToCart = () => {
//Add a default version here
};
const getState = () => {
//Add a default version here
};
// Get the custom values and override with instance ones.
const value = {addToCart, getState, ...props.value}
return <MinicartContext.Provider value={value}>
{props.children}
</MinicartContext.Provider>
}
Then when using the provider:
const SomeComponent = props => {
const addToCart = () => {
//A custom version used only in this component, that need to override the default one
};
//Use the Wrapper, forget the MinicartContext.Provider
return <MinicartProvider value={{addToCart}}>
/* Stuff */
</MinicartProvider>
}
And when using the consumer you have three options:
Class Components with single context
export default class AddToCartButton extends Component {
static contextType = MinicartContext;
render (){
const {addToCart, getState} = this.context;
return (/*Something*/)
}
}
Class Components with multiple contexts
export default class AddToCartButton extends Component {
render (){
return (
<MinicartContext.Consumer>{value => {
const {addToCart, getState} = value
return (/*Something*/)
}}</MinicartContext.Consumer>
)
}
}
Functional Components
const AddToCartButton = props => {
const {addToCart, getState} = useContext(MinicartContext);
}
You can create the Wrapper Provider as a class component too, and pass the full state as value, but it's unnecessary complexity.
I Recommend you take a look at this guide about contexts, and also, avoid using the same name on the same scope... Your AddToCartButton.jsx file was reeeeally confusing :P
The issue I had was that I was using <MinicartContainer> in multiple places but all should act as one and the same. Changing it so it wrapped all elements made other elements reset their state when the context updated.
So the only solution I found was to make everything static (including state) inside MinicartContainer, and keep track of all the instances and then use forceUpdate() on all (needed) instances. (Since I am never doing this.setState nothing ever updates otherwise)
I though the new React context would be a clean replacement for things like Redux but as it stands today it's more a really vague specification which can replace Redux in a (sometimes) non standard way.
If you can just wrap all child Consumers with a single Provider component without any side-effects then you can make it a more clean implementation. That said I don't think what I have done is bad in any way but not what people expect a clean implementation should look like. Also this approach isn't mentioned in the docs at all either.
In addition to Toug's answer, I would memoize the exposed value prop of the provider. Otherwise it will re-render it's subscribers every time even if the state doesn't change.
export const MinicartContext = React.createContext({}) // Export the Context so we can use the Consumer in class and functional components (above). Don't use the Provider from here.
// Wrap the provider to add some custom values.
export const MinicartProvider = props => {
const addToCart = () => {
//Add a default version here
};
const getState = () => {
//Add a default version here
};
// Get the custom values and override with instance ones.
const value = useMemo(
() => ({addToCart, getState, ...props.value}),
[addToCart, getState, props.value]
);
return <MinicartContext.Provider value={value}>
{props.children}
</MinicartContext.Provider>
}
I have a stateless functional component which has no props and populates content from React context. For reference, my app uses NextJS and is an Isomorphic App. I'm trying to use React.memo() for the first time on this component but it keeps re-rendering on client side page change, despite the props and context not changing. I know this due to my placement of a console log.
A brief example of my component is:
const Footer = React.memo(() => {
const globalSettings = useContext(GlobalSettingsContext);
console.log('Should only see this once');
return (
<div>
{globalSettings.footerTitle}
</div>
);
});
I've even tried passing the second parameter with no luck:
const Footer = React.memo(() => {
...
}, () => true);
Any ideas what's going wrong here?
EDIT:
Usage of the context provider in _app.js looks like this:
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
...
return { globalSettings };
}
render() {
return (
<Container>
<GlobalSettingsProvider settings={this.props.globalSettings}>
...
</GlobalSettingsProvider>
</Container>
);
}
}
The actual GlobalSettingsContext file looks like this:
class GlobalSettingsProvider extends Component {
constructor(props) {
super(props);
const { settings } = this.props;
this.state = { value: settings };
}
render() {
return (
<Provider value={this.state.value}>
{this.props.children}
</Provider>
);
}
}
export default GlobalSettingsContext;
export { GlobalSettingsConsumer, GlobalSettingsProvider };
The problem is coming from useContext. Whenever any value changes in your context, the component will re-render regardless of whether the value you're using has changed.
The solution is to create a HOC (i.e. withMyContext()) like so;
// MyContext.jsx
// exported for when you really want to use useContext();
export const MyContext = React.createContext();
// Provides values to the consumer
export function MyContextProvider(props){
const [state, setState] = React.useState();
const [otherValue, setOtherValue] = React.useState();
return <MyContext.Provider value={{state, setState, otherValue, setOtherValue}} {...props} />
}
// HOC that provides the value to the component passed.
export function withMyContext(Component){
<MyContext.Consumer>{(value) => <Component {...value} />}</MyContext.Consumer>
}
// MyComponent.jsx
const MyComponent = ({state}) => {
// do something with state
}
// compares stringified state to determine whether to render or not. This is
// specific to this component because we only care about when state changes,
// not otherValue
const areEqual = ({state:prev}, {state:next}) =>
JSON.stringify(prev) !== JSON.stringify(next)
// wraps the context and memo and will prevent unnecessary
// re-renders when otherValue changes in MyContext.
export default React.memo(withMyContext(MyComponent), areEqual)
Passing context as props instead of using it within render allows us to isolate the changing values we actually care about using areEqual. There's no way to make this comparison during render within useContext.
I would be a huge advocate for having a selector as a second argument similar to react-redux's new hooks useSelector. This would allow us to do something like
const state = useContext(MyContext, ({state}) => state);
Who's return value would only change when state changes, not the entire context.
But I'm just a dreamer.
This is probably the biggest argument I have right now for using react-redux over hooks for simple apps.
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
I was reading the documentation for the Redux library and it has this example:
In addition to reading the state, container components can dispatch actions. In a similar fashion, you can define a function called mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
This actually makes no sense. Why do you need mapDispatchToProps when you already have mapStateToProps?
They also provide this handy code sample:
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
What is this function and why it is useful?
I feel like none of the answers have crystallized why mapDispatchToProps is useful.
This can really only be answered in the context of the container-component pattern, which I found best understood by first reading:Container Components then Usage with React.
In a nutshell, your components are supposed to be concerned only with displaying stuff. The only place they are supposed to get information from is their props.
Separated from "displaying stuff" (components) is:
how you get the stuff to display,
and how you handle events.
That is what containers are for.
Therefore, a "well designed" component in the pattern look like this:
class FancyAlerter extends Component {
sendAlert = () => {
this.props.sendTheAlert()
}
render() {
<div>
<h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
<Button onClick={sendAlert}/>
</div>
}
}
See how this component gets the info it displays from props (which came from the redux store via mapStateToProps) and it also gets its action function from its props: sendTheAlert().
That's where mapDispatchToProps comes in: in the corresponding container
// FancyButtonContainer.js
function mapDispatchToProps(dispatch) {
return({
sendTheAlert: () => {dispatch(ALERT_ACTION)}
})
}
function mapStateToProps(state) {
return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}
export const FancyButtonContainer = connect(
mapStateToProps, mapDispatchToProps)(
FancyAlerter
)
I wonder if you can see, now that it's the container 1 that knows about redux and dispatch and store and state and ... stuff.
The component in the pattern, FancyAlerter, which does the rendering doesn't need to know about any of that stuff: it gets its method to call at onClick of the button, via its props.
And ... mapDispatchToProps was the useful means that redux provides to let the container easily pass that function into the wrapped component on its props.
All this looks very like the todo example in docs, and another answer here, but I have tried to cast it in the light of the pattern to emphasize why.
(Note: you can't use mapStateToProps for the same purpose as mapDispatchToProps for the basic reason that you don't have access to dispatch inside mapStateToProp. So you couldn't use mapStateToProps to give the wrapped component a method that uses dispatch.
I don't know why they chose to break it into two mapping functions - it might have been tidier to have mapToProps(state, dispatch, props) IE one function to do both!
1 Note that I deliberately explicitly named the container FancyButtonContainer, to highlight that it is a "thing" - the identity (and hence existence!) of the container as "a thing" is sometimes lost in the shorthand
export default connect(...)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
syntax that is shown in most examples
It's basically a shorthand. So instead of having to write:
this.props.dispatch(toggleTodo(id));
You would use mapDispatchToProps as shown in your example code, and then elsewhere write:
this.props.onTodoClick(id);
or more likely in this case, you'd have that as the event handler:
<MyComponent onClick={this.props.onTodoClick} />
There's a helpful video by Dan Abramov on this here:
Redux: Generating Containers with connect() from React Redux (VisibleTodoList)
mapStateToProps() is a utility which helps your component get updated state(which is updated by some other components),
mapDispatchToProps() is a utility which will help your component to fire an action event (dispatching action which may cause change of application state)
mapStateToProps, mapDispatchToProps and connect from react-redux library provides a convenient way to access your state and dispatch function of your store. So basically connect is a higher order component, you can also think as a wrapper if this make sense for you. So every time your state is changed mapStateToProps will be called with your new state and subsequently as you props update component will run render function to render your component in browser. mapDispatchToProps also stores key-values on the props of your component, usually they take a form of a function. In such way you can trigger state change from your component onClick, onChange events.
From docs:
const TodoListComponent = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
const TodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Also make sure that you are familiar with React stateless functions and Higher-Order Components
Now suppose there is an action for redux as:
export function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
When you do import it,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: () => { // handles onTodoClick prop's call here
dispatch(addTodo())
}
}
}
export default connect(
null,
mapDispatchToProps
)(Greeting);
As function name says mapDispatchToProps(), map dispatch action to props(our component's props)
So prop onTodoClick is a key to mapDispatchToProps function which delegates furthere to dispatch action addTodo.
Also if you want to trim the code and bypass manual implementation, then you can do this,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.addTodo();
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
export default connect(
null,
{addTodo}
)(Greeting);
Which exactly means
const mapDispatchToProps = dispatch => {
return {
addTodo: () => {
dispatch(addTodo())
}
}
}
mapStateToProps receives the state and props and allows you to extract props from the state to pass to the component.
mapDispatchToProps receives dispatch and props and is meant for you to bind action creators to dispatch so when you execute the resulting function the action gets dispatched.
I find this only saves you from having to do dispatch(actionCreator()) within your component thus making it a bit easier to read.
React redux: connect: Arguments