React global state with hooks - reactjs

I am trying to create a global state variable with hooks. My simple test code works perfectly, but the browser gives me warning:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
import React, {createContext, useState, useContext} from 'react';
const TextContext = createContext();
function WpApp (props) {
const [text1, setText1] = useState('this is default');
return (
<div>
<TextContext.Provider value={[text1, setText1]}>
<Dummy />
</TextContext.Provider>
</div>
);
}
function Dummy () {
const [text1, setText1] = useContext(TextContext);
return (
<div>
<div>{text1}</div>
<button onClick={() => setText1('This is new text')}>
Set new
</button>
</div>
);
}
The warning is caused by the state variable in Context.Provider value:
<TextContext.Provider value={[text1, setText1]}>
The code works perfectly but the warning is worrying. Is there an error in this code and/or is there a fix that removes this warning?

Related

How to access variable declared inside a functional component in other functions

Stuffing the functions inside FoodSearch() isn't good practice since the other functions would be defined every time the component is rendered. However, when I put the functions outside of FoodSearch(), the variables become undefined.
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
function handleChange(e) {
setQuery(e.target.value);
}
function handleClick(e) {
// e.preventDefault();
history(`/food/${query}`);
}
function FoodSearch() {
const [query, setQuery] = useState('');
const history = useNavigate()
return (
<div>
<h1>Search For A Food!</h1>
<input
type='text'
placeholder='search for a food'
value={query}
onChange={handleChange}
/>
<Link to={`/food/${query}`}>Go!</Link>
<button onClick={handleClick}>Save New Food!</button>
</div>
);
}
export default FoodSearch;
I'm a beginner in react and trying to learn
You should define them inside FoodSearch but wrap in a useCallback to memoize the returned function. That way they won't change unless a dependency (e.g. query) changes.

React function component with functions

Is it possible to create an instance of a function component like regular classes like c# and JAVA where you can call functions on the component? Something like:
https://codesandbox.io/s/hungry-microservice-bp292?file=/src/App.js
It must be an instance so that the component can be used multiple places with its own instance and values. Not like a static class.
import React from "react";
import "./styles.css";
import MyFunc from "./MyFunc";
export default function App() {
const addAlert = () => {
MyFunc.addAlert("dasdsad");
};
return (
<div className="App">
<button onClick={addAlert}>Add alert</button>
<MyFunc />
</div>
);
}
You are mixing different concepts of ReactJS and the underlying DOM. It is not possible to get a ref on the functional component itself. At most you can use forwardRef to get a reference to the underlying DOM element. You can read more about that Refs and the DOM and Forwarding Refs.
With that in mind you could change your approach by uplifting the state to the parent e.g.
App.js
export default function App() {
const [alerts, addAlerts] = useState(["Alert1", "Alert2"]);
const addAlert = () => {
addAlerts(alerts.concat("dasdsad"));
};
return (
<div className="App">
<button onClick={addAlert}>Add alert</button>
<MyFunc alerts={alerts}/>
</div>
);
}
MyFunc.js
const MyFunc = props => {
return (
<>
{props.alerts && props.alerts.map((alert, index) => (
<div>{alert}</div>
))}
</>
);
};
export default MyFunc;

How to wrap a function which contains hooks with HoC

As the title suggest I want to be able to wrap a function (which contains) hooks in a HoC.
In the example below I want to be able to wrap the JSX from TestView with div element tag where the className="someClassName". However I get the following exception:
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 for tips about how to debug and
fix this
import React, { Component } from 'react'
function wrap(component) {
let calledComponent = component()
return (
<div className="someClassName">
{calledComponent}
</div>
);
}
function TestView() {
const [ val, setValue] = React.useState('Initial Value');
return (
<div>
<input type="text" value={val} onChange={event=>setValue(event.target.value)}/>
</div>
)
}
export default wrap(TestView);
Concretely, a higher-order component is a function that takes a
component and returns a new component.
react docs
so, you have to have a function that returns a component, maybe like this.
import React, { useState } from 'react';
import '../styles.css';
const withStyle = WrappedComponent => {
return function WithStyle() {
return (
<div className='myClassStyle'>
<WrappedComponent />
</div>
);
};
};
function TestView() {
const [val, setVal] = useState('Initial Value');
return (
<div>
<input type='text' value={val} onChange={e => setVal(e.target.value)} />
</div>
);
}
export default withStyle(TestView);

ReactJs: Prevent Rerender of wrapped component

I'm trying to prevent a re-render when using custom hook for hours now -.-, need some help ;O|
(Dont know if I should call this custom hook or functional hoc though)
I have a MessageList component that display a SimpleMessage wrapped in WithAvatarHeader.
Here is my profiler result:
Every time I add a message to the list, all messages are rendered again.
This isn't happening when I only use SimpleMessage in MessageList
Is there a way to memo(WithAvatarHeader) ?
MessageList :
import React from "react";
import SimpleMessage from "./SimpleMessage";
import WithAvatarHeader from "./WithAvatarHeader";
const MessageList = props => {
const Message = WithAvatarHeader(SimpleMessage);
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
SimpleMessage:
import React, { memo } from "react";
const SimpleMessage = props => {
return (
<div className="simple-message">
{props.message}
</div>
);
};
export default memo(SimpleMessage);
WithAvatarHeader:
import React from "react";
const WithAvatarHeader = WrappedComponent => props => {
return <WrappedComponent {...props} />;
};
export default WithAvatarHeader;
Thanks for the help :-)
You should not declare component inside another component.
Once you move declaration outside:
const Message = WithAvatarHeader(SimpleMessage);
const MessageList = props => {
return (
<div className="message-list">
{props.messages.map(message => {
return <Message message={message} key={message._id}/>;
})}
</div>
);
};
you will be fine.
Reason is reconciliation process that decides what's to drop, what to create and what to update.
Besides your JSX says it still same element <Message> React checks component's constructor(it does not work with text representation from JSX). And it will referentially different(since you re-declare this constructor on next render). So React drops every <Message> and create them from scratch. Keeping declaration outside your MessageList means constructor is referentially the same so React will not re-create <Message> till key is the same.

React hooks not working when imported from local library

I'm using React to import a function with a useState hook and that seems to break it. I have a version of react with hooks:
npm ls react => react#16.10.2
npm ls react-dom => react-dom#16.10.2
I can use components fine. When I include a hooks, I get the "Invalid hook call" screen.
In my library project I have:
/**
* #class ExampleComponent
*/
import * as React from 'react'
import styles from './styles.css'
export default function ThingyDefault() {
return <p>hi</p>
}
export type Props = { text: string }
export class ExampleComponent extends React.Component<Props> {
render() {
const {
text
} = this.props
return (
<div className={styles.test}>
Example Component: {text}
</div>
)
}
}
////////////////// THIS DOESN'T SEEM TO WORK //////////////////
export function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In my project that uses that library:
import React from 'react';
import './App.css';
import ThingyDefault, {ExampleComponent, Example} from 'thingy';
const App: React.FC = () => {
return (
<div>
<ThingyDefault />
<ExampleComponent text='hello' />
{/* commenting this component out makes it work */}
<Example />
</div>
);
}
export default App;
What am I doing wrong here?
You are not adhering to the Rules of Hooks, specifically in your case, calling a hook from a standard javascript function.
Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you can:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
By following this rule, you ensure that all stateful logic in a component is clearly visible from its source code.
Since it seems to be a import/export issue, try changing your export to this:
const Example = () => {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = React.useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
export { Example };
You're not doing anything wrong there.
It should work as intended. Check the example Stackblitz here, with the same version of React that you have.
I might re-check the app for any duplicate dependencies messing up the functionality of the hooks. Especially, it's unable to determine that your function Example(), is indeed a functional component.

Resources