React.useState react differently as a prop or as a property? - reactjs

and my deepest appologies for the questions spam (I really have troubles understanding React learning it alone). In my actual code, I have created a context, specified to be able to change:
const ThemeContext = React.createContext(["light", () => {}]);
In my app function, I create a hook (I still doesn't clearly understand why, see my very last bonus question) on it so I'll be able to access those data in child components:
const themeHook = React.useState("light");
return (<ThemeContext.Provider value={themeHook}>...</ThemeContext.Provider>);
The component where I use it, I had to encompass it in a function to be able to to pass the hook data though props:
function ThemeTogglerFunction() {
const [contextType, toggleTheme] = React.useContext(ThemeContext);
return <ThemeToggler context={contextType} toggler={toggleTheme} />;
}
And finally, my ThemeToggler component can use that theme data and even switch between the two values I specified, I'm happy:
class ThemeToggler extends React.Component {
constructor(props) {
super(props);
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() { this.props.toggler(this.props.context === "light" ? "dark" : "light"); }
render() { return (<span style={ownStyle} onClick={this.handleToggle}>Toggle actuel {this.props.context} theme</span>); }
}
But then, if I insert the props into local properties (which sounds more proper to me):
class ThemeTogglerLocal extends React.Component {
constructor(props) {
super(props);
this.gloablThemeContext = props.context;
this.gloablThemeToggler = props.toggler;
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() { this.gloablThemeToggler(this.gloablThemeContext === "light" ? "dark" : "light"); }
render() { return (<span style={ownStyle} onClick={this.handleToggle}>Toggle actuel {this.gloablThemeContext} theme</span>); }
}
even if the hook value is correctly changed one ("light" becomes"dark") in the global ThemeContext (I can see it as everything is rerendered with the correct color change), the said local properties is not changed (is stays on "light") and so the app stays in "dark" mode from there. I suppose that is because the property is initialised in the constructor and have no more link with the hook, is it correct ? Does it mean that when you "import" a hook into a component, you shouldn't save it into local properties ?
Furthermore (small bonus question) : I had to create a hook in my app function, and I don't understand why, as React.createContext() already creates something which looks like a hook. Why can't I pass it directly the Theme Context.Provider (<ThemeContext.Provider value={ThemeContext}> in place of <ThemeContext.Provider value={themeHook}>) ?
Thank you very much in advance, and sorry for the length of this post.

Given
class ThemeTogglerLocal extends React.Component {
constructor(props) {
super(props);
this.gloablThemeContext = props.context;
this.gloablThemeToggler = props.toggler;
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.gloablThemeToggler(
this.gloablThemeContext === 'light' ? 'dark' : 'light',
);
}
render() {
return (
<span onClick={this.handleToggle} style={ownStyle}>
Toggle actuel {this.gloablThemeContext} theme
</span>
);
}
}
Even if the hook value is correctly changed one ("light"
becomes"dark") in the global ThemeContext (I can see it as everything
is rerendered with the correct color change), the said local
properties is not changed (is stays on "light") and so the app stays
in "dark" mode from there. I suppose that is because the property is
initialised in the constructor and have no more link with the hook, is
it correct ?
Correct, in the class-based component you would need to implement the componentDidUpdate lifecycle method to "react" to the prop values changing in order to update the class properties.
componentDidUpdate(prevProps) {
if (prevProps.context !== this.props.context( {
this.gloablThemeContext = props.context;
this.gloablThemeToggler = props.toggler;
}
}
Does it mean that when you "import" a hook into a
component, you shouldn't save it into local properties?
This is exactly correct. Storing passed props into local component state (or class variables in your case) is an anti-pattern in React. It often leads to stale state/enclosures/etc and buggy code. You should consume passed props directly in the rendered result or any other lifecycle methods.
Furthermore (small bonus question): I had to create a hook in my app
function, and I don't understand why, as React.createContext() already
creates something which looks like a hook. Why can't I pass it
directly the Theme Context.Provider (<ThemeContext.Provider value={ThemeContext}> in place of <ThemeContext.Provider value={themeHook}>)?
Recall that your context value is ["light", () => {}]:
const ThemeContext = React.createContext(["light", () => {}]);
When you create the actual value in the provider const themeHook = React.useState("light"); it basically resolves to const [theme, setTheme] = React.useState("light");
You are passing the current state value and the state updater function as the context value to the provider.
return (
<ThemeContext.Provider value={[theme, setTheme]}>
...
</ThemeContext.Provider>
);
This is destructured in consumers as
const [theme, setTheme] = React.useContext(ThemeContext);
or in your case
const [contextType, toggleTheme] = React.useContext(ThemeContext);

Related

How to initialize the react functional component state from props

I'm using React hooks for app state, I wondered about how to initialize the functional component state using props? The useState hook doc says something definitive like,
const [count, setCount] = useState(0);
I want to initialize that 0 value by the value of props being passed to the component. The Older as,
import React from 'react';
export default class Sym extends React.Component{
constructor(props){
super(props);
this.state = {
sym : [0,3,2,8,5,4,1,6],
active: this.props.activeSym
}
this.setActive = this.setActive.bind(this);
}
setActive(itemIndex){
this.setState({
active: itemIndex
});
}
render(){
return (
<div><h1>{ this.state.sym[this.state.active]}</h1></div>
);
}
}
works fine. Where the parent Component passes activeSym prop and Sym component initializes the state with it using this.props.activeSym in constructor. Is there any workaround to achieve same in function component?
First you can define it from props (if the prop exist):
const [count, setCount] = useState(activeSym);
And then you can update this value, when prop doesn't have a value immediately (when component rendered):
useEffect(() => {
if (activeSym) {
setCount(activeSym);
}
}, [activeSym])
Yes, this can be possible with functional component too! You just need to add useEffect to listen to prop change for initializing state with prop value
export const newComponent = (props) => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
useEffect(() => {
setVal(value);
}, [value]);
return <div>{val}</div>;
};
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
Yes you can first define state using props:
const [name, setName] = useState(props.obj?.name);
And then you can if the state is still undefined means props doesn't have a value, then:
useEffect(() => {
if (JSON.stringify(props.obj) !== "{}") {
setName(props.obj?.name);
}
}, [props.obj])
Just as follows :
const MyFunctionalComponent = ({myProp}) => {
const [count, setCount] = useState(myProp)
return (
/* ... */
)
}
There are two ways you can change the state:
one is using this.state and
another one is this.setState.
We use the first method to initialize the state in the constructor, and the second method is used for the rest of the time.
Initialize State in the Constructor
One way is to initialize the state is in the constructor. As we discussed earlier constructor is the first method to be called when React instantiates the class. This is the perfect place to initialize the state for the component because the constructor is called before the React renders the component in the UI.
class WithConstructor {
constructor() {
this.state = {
name: "StackOverflow"
}
}
}
Initialize State Without Constructor
Another way of initializing state in React is to use the Class property. Once the class is instantiated in the memory all the properties of the class are created so that we can read these properties in the render function.
class WithoutConstructor {
state = {
name: "StackOverflow"
}
}

How to create a global variable that can be accessed in different component in React?

I am getting username from server and i want to use the same user name from some other component. I know session Storage is one of the way to deal with it but i dont want to use for security reason. How can we create a global object in react?
// most simplistic
window.myAppData = {
userName: 'chad123',
language: 'EN',
// other stuff
};
window.myAppData.userName // 'chad123'
But most apps require something a bit more complex. You could use React context.
https://reactjs.org/docs/context.html
Context provider
// create context provider and consumer
const UserContext = React.createContext();
export default UserContext;
// wrap part of your app (or whole app)
// with Provider that needs access to user
class App extends React.Component {
constructor() {
super();
this.state = {
user: null
};
}
componentDidMount() {
yourUserAPI().then(user => this.setState({ user }));
}
render() {
return (
<UserContext.Provider value={this.state.user}>
<MyComponent />
</UserContext.Provider>
);
}
}
Context consumer
A) Current standard
// use anywhere in your app like this
// PS! must be descendant of Provider
class MyComponent extends React.Component {
render() {
return (
<UserContext.Consumer>
{user => {
// do stuff with 'user'
}}
</UserContext.Consumer>
);
}
}
B) React Hooks (IN ALPHA)
// only works with functional
// components (no classes)
function MyComponent() {
const user = React.useContext(UserContext.Consumer);
// do stuff with 'user'
return 'something';
}
I think to achieve that you need to use "React's context API"
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
For further info do visit the link React context api
You need a global state management like Redux.
Once you have this setup you can map your global state to your local component props and access it like you do any other prop: this.props.globalUsername.
I recommend you learn Redux by following their example program on the official website https://redux.js.org/basics/exampletodolist
Well you can create a global variable in ReactJS but it doesn't make it more "secure" over Session/Local storage.
I think creating a global variable in React project is not the best practice at all because of this simply reason: Should components track down this variable for any change ? If the answer is yes, what you are looking at should be "How to manage global state in React" not "How to create a Global Variable in React".
You can achieve it with Redux. As official documentation says "Redux is a predictable state container" but you can think it as Global State Container for your app.
You can check redux out from that url: https://redux.js.org/
USE CUSTOM HOOKS
It is very simple
globals.js
let _obj = {}
export const setGlobal = (obj) => {
Object.assign(_obj, obj)
}
export const getGlobal = varName => {
if(_obj[varName] !== undefined){
return _obj[varName]
}
else {
return null
}
}
component1.jsx
import React.....
import { setGlobal } from "./globals";
import.....
setGlobal({ title : "welcome" })
class comp.... {
render{
return(){
<i onClick={()=>setGlobal({location: "House"})}>Cmponent1</i>
}
}
}
module exp...
Component2.jsx
import React.....
import { setGlobal, getGlobal } from "./globals";
import.....
setGlobal({ greet : "Hi"})
class comp.... {
render{
return(){
<i>{getGlobal("greet")}, {getGlobal("title")} to our {getGlobal("location")}</i>
}
}
}
module exp...

React Context: passing all of a component's methods at once

Suppose I have a container component for handling app logic which has a lot of methods:
class ScreenContainer extends React.Component
{
state = {
inputs: { /* various properties for input values */ },
thingyActive: false,
someList: ["thing1", "thing2"],
// ...etc.
};
handleInputChange = e => {
const { name, value } = e.target;
this.setState(prevState => ({
inputs: { ...prevState.inputs, [name]: value }
}));
};
toggleThingy = () => this.setState(prevState => ({
thingyActive: !prevState.thingyActive
}));
coolMethod = () => { /* Do cool stuff */ };
boringMethod = () => { /* Do boring stuff */ };
// ...more methods...
}
I need ALL of these methods to be accessible to inner components. I'll use a Context provider in this example, and we'll just say that the context gets consumed by various nested presentational components making up a screen in the application.
const ScreenContext = React.createContext();
To pass methods either down to a child component or into a context provider value, it seems you always end up having to do something like below (note that I'm lifting the "actions" into state in this example per the advice given in the React documentation).
class ScreenContainer extends React.Component
{
constructor()
{
super();
this.state = {
// ...same state as before, plus:
actions: {
handleInputChange: this.handleInputChange,
toggleThingy: this.toggleThingy,
coolMethod: this.coolMethod,
boringMethod: this.boringMethod,
everySingleOtherMethod: this.everySingleOtherMethod,
// ...on and on
}
};
}
// ...same methods as before...
render()
{
return (
<ScreenContext.Provider value={this.state}>
{this.props.children}
</ScreenContext.Provider>
);
}
I was looking for a way to avoid passing them all one by one. A possible solution I found involves using a getter and looping through the class instance properties like so:
get allMethods()
{
let output = {};
for (var prop in this)
{
if (this.hasOwnProperty(prop) && typeof this[prop] === "function")
output[prop] = this[prop];
}
return output;
}
Then I can just do:
// (in ScreenContainer constructor)
this.state = {
// ...state,
actions: this.allMethods
};
The getter code could also be extracted out into a utility function for reuse in other container-type components if needed. Obviously, this is only worthwhile if there are a ton of methods to be passed down.
It seems simple enough and appears to work just fine as long as it's done in the contructor. Is there anything crazy about this? Is it bad practice in any way, or does it have any potential side effects I'm not aware of? Is there maybe a better way I'm missing?
EDIT
I've updated the example to be closer to my real code; it now shows what kinds of things the methods might do and uses a Context setup rather than passing the methods down as props to a single child component.
If a class doesn't maintain a state, and class methods are supposed to be used separately as helper functions, they shouldn't be a part of the class, let alone class component. A class acts as namespace in this case. In modern JavaScript, modules are used as namespaces. It can be:
export const coolMethod = () => { /* Do cool stuff */ };
export const coolerMethod = () => { /* Do even cooler stuff */ };
export const boringMethod = () => { /* Do boring but necessary stuff */ };
ScreenContainer component is an example of 'smart' container component. It's always preferable to list passed functions explicitly rather than pass them all automatically. ScreenContainer may get private methods at some point. And there should be a guarantee that lifecycle hooks won't be passed accidentally, too.
If it is supposed to have a single child, it can be applied as higher-order component:
const withScreen(Comp) => {
return class ScreenContainer extends React.Component {
...
render() {
return <Comp handleInputChange={this.handleInputChange} /* ... */ />;
}
}
}
In this particular case render can be distinguished from passed functions because the latter are instance methods (arrow functions). While this kind of magic generally isn't recommended because it may cause problems and won't work properly for private methods, it can be shortened to:
render() {
const fns = {};
for (const method of Object.keys(this)) {
if (typeof this[method] === 'function')
fns[method] = this[method];
}
return <Comp {...fns} {...this.props} />;
}
For multiple children, ScreenContainer children could be traversed to add props in a similar way.
For indirect children, context API can be used to pass functions.
While it's possible to pass ScreenContainer this to children, this isn't recommended because this breaks the encapsulation and contradicts the principle of least privilege.
One way I've done this is to instantiate a new instance in the constructor of the child component like this:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.Container = new MyContainer();
}
Then you can use any methods like:
this.Container.coolMethod()
Edit
I misunderstood. I've only done this by creating a helper class that you instantiate, not a component. It is helpful when you have methods you want to use in multiple components without having to pass all your methods as props through the component tree.

Is it ok to use a wrapper component to pass props in React?

export function injectProps() {
const injects = {store: new Store()}; // some store
return function (Component) {
return class Proxy extends React.Component {
render() {
return React.createElement(Component, {
...injects,
...this.props,
});
}
};
}
}
Is it ok to use this instead of Redux or Context API with React?
Update: I think I missed to point out my expectation. I'm actually passing some service(http, localStorage) to childrens only when they asks for it. It's not only about the store as services don't have any state. But I also need to pass store through it.
https://pastebin.com/G3PgVxLn
Maybe this tweet by the Dan Abramov (React maintainer) might help.
I understand it was probably not the point of the article. But I see
people reaching for Context or Redux because they don’t realize
components can take any children — and that often removes the need for
deep prop passing. Would be great to highlight!
And Dave Ceddia posted a relavant React documentation link.
Composition vs Inheritance
You can read upon those two.
And here is a demo Nicolas Marcora created to show me how to pass properties to child/children.
You can pass props to children using React.cloneElement(child,...
Working demo on StackBlitz.
export default class WithMouse extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = event => { ... }
render() {
const { children } = this.props
const childElements = React.Children.map(children, child =>
React.cloneElement(child, {
mouse: this.state,
onMouseMove: this.handleMouseMove
})
)
return <div>
{ childElements }
</div>
}
}
You can use WithMouse class to pass props downward to all children and use it like following.
class App extends Component {
...
render() {
return (
<WithMouse>
<MouseTracker />
</WithMouse>
);
}
}
MouseTracker has access to props passed from WithMouse so you can just use it without directly passing it manually.
You can probably go further and pass all props instead of a few (mouse, onMouseMove)

react component - parent child interaction; component Lifecycle

I am developing a simple browser app to get some specific data from the user.
I have several components to simplify the proccess of collecting that data, but I am currently rethinking my way of rendering this component.
Basically, i have my main component, which uses state to keep track of which component to render.
I am doing this for almost all of my components.
Also, i also have a function inside the parent component, that i pass to the child component via props, and that is used as a callback to pass the child state to its parent, when that component is no longer useful.
class Main extends React.Component {
constructor(props){
this.state = {
renderA: true,
renderB: false,
childState: null
}
}
collectState(state){
this.setState({
childState: state
});
}
render() {
let content = null;
if(this.state.renderA === true){
content = <ComponentA />
} else {
content = <ComponentB />
}
return(
<div>
{content}
</div>);
}
}
So, using the above example, the child would be something like this
class ComponentA extends React.Component {
constructor(props){
super(props);
this.state = {
stop: false,
usefullInfo: null
}
destroy() {
this.props.collectstate(this.state.usefullInfo)
}
render(){
render something (like a Form) untill this.state.usefullInfo is set;
in that case, set this.state.stop true which will call destroy, passing the usefull information to parent
}
}
So, this method works for me, but i can see clearly that most probably this is not the way to do this.
At this point my question are:
1) how can I stop rendering a component without having to track it with some property like this.state.stop ?
2) if i want to render 2 different components, like in the main component, do I always have to keep a renderA and renderB property on state, to render one or another?
3) is there a better way to pass information from child to parent? i am currently using a callback function passed via props from parent to child, and i am invoking that callback when the component has done its purpose
4) any general suggestions on how to improve the quality of the above code?
Thank you for you help :)!
Your example works fine, but in React it is recommended to lift state up when handling data from multiple children (source). So I would recommend to keep the sate of every children in the parent, and pass props with values and handlers to the children.
Here's a sample app you can check. The form components handle the case you want to implement.
To answer your questions:
The parent component should decide, based on its own state, whether to render a child component or not.
It's not needed to keep variables on state about what component to render. that should be computed in render() based on the parent's state
Yes, callback are the recommended way to pass information to parents
Code quality looks good. You can always do good with tools like prettier or ESlint.
Here's an example:
class Main extends React.Component {
constructor(props) {
this.state = {
stateA: '',
stateB: '',
};
}
handleStateChange(name, value) {
this.setState({
[name]: value,
});
}
render() {
const { stateA, stateB } = this.statel;
const shouldRenderA = !stateA;
if (shouldRenderA) {
return <ComponentA value={stateA} onChange={value => this.handleStateChange('stateA', value)} />;
}
return <ComponentB value={stateA} onChange={value => this.handleStateChange('stateB', value)} />;
}
}
class ComponentA extends React.Component {
render() {
const { value, onChange } = this.props;
return <input type="text" value="value" onChange={onChange} />;
}
}

Resources