Usage of State in functional component of React - reactjs

How to use State in functional component of React ?
I have a component like below
import React, { useState } from 'react';
function App() {
const [count] = React.useState(0); //Set State here
function count_checkbox() {
var applicable = [];
const cbs = document.querySelectorAll('input[class*="checkbox_value"]');
cbs.forEach((cb) => {
if (cb.checked) {
applicable.push(parseInt(cb.value));
}
});
this.setState({ count: applicable.length }) //Update State here
}
return (
<div className="App">
<input type="submit" id="button" value="{ this.state.count }"/> //Use State here
</div>
);
}
export default App;
But State is not working here.

states should be like this
const [count,setCount] = React.useState(0); //setState
function count_checkbox() {
var applicable = [];
const cbs = document.querySelectorAll('input[class*="checkbox_value"]');
cbs.forEach((cb) => {
if (cb.checked) {
applicable.push(parseInt(cb.value));
}
});
setCount(applicable.length) //Update State here
}
setCount sets the state count.

Updating the state in a functional component is different from a class component. The useState hook returns an array consisting of the value and a setter.
You then call the setter function to update the value of the state.
const [count, setCount] = React.useState(0);
function count_checkbox() {
...
setCount(applicable.length)
}

this.setState is unsed to update state in class components. In order to update state in functional component you need to update it with appropriate function.
useState returns 2 values first is current state value and second one function to update that state
const [count, setCount] = React.useState(0);
Whenever you want to update count state you need to update with setCount function
function count_checkbox() {
...
setCount(applicable.length); //Update State here
}
You can access count in your useEffect like below:-
useEffect(() => {
alert(count);
}, [count])

A functional component is just a plain javascript function which takes props as an argument and returns a react element.
A stateless component has no state, it means that you can’t reach this.state and this.setState() inside it. It also has no lifecycle so you can’t use componentDidMount and other hooks.
import React, { useState } from 'react';
function App() {
const [count, setCount] = React.useState(0); //Set initial state here
function count_checkbox() {
var applicable = [];
const cbs = document.querySelectorAll('input[class*="checkbox_value"]');
cbs.forEach((cb) => {
if (cb.checked) {
applicable.push(parseInt(cb.value));
}
});
setCount(applicable.length) //Update State here
}
return (
<div className="App">
<input type="submit" value={`Apply tax to ${ count } item(s)`} /> //You Can't use this.state.count here
</div>
);
}
export default App;

Related

How to change useState from inside of Child Component

I am trying to import CSVs inside a Importer Component and pass on the Data to the Parent and change useState there...
So here i am trying to call said Component and pass on the useState function.
const [database, setDatabase] = useState([]);
useEffect(() => {
<Importer setdata={(data) => setDatabase([...data])} />;
}, []);
and Child Component is importing the CSV and passing on the data to be displayed after changing the State with useState:
const importAllCsv = (props) => {
text("template.csv").then((data) => {
//console.log(data);
const psv = dsvFormat(";");
//console.log(psv.parse(data));
DATABASE = psv.parse(data);
console.log(DATABASE);
props.setdata(DATABASE);
});
};
export default function Importer(props) {
return importAllCsv(props);
}
Components must start with a capital letter, also avoid returning components in useEffect when you can return them in the return part of the parent component.
As Aliyan said, try props.setdata((prevState) => [...prevState, ...DATABASE])
As per my understanding, you want to update the parent's state through a child component, for this you can simply pass the currentState (if required) and the setState function to the child as a prop using the following method :
export default function App() { //Parent Component
const [state, setState] = useState(1);
return (
<div className="App">
<div>{state}</div>
<ChildComponent setParentState={setState} currentState={state}/>
</div>
);
}
function ChildComponent({ setParentState, currentState }) {
function UpdateState() {
setParentState(currentState+1);
}
return <button onClick={() => UpdateState()}>Update State</button>;
}
Try to:
props.setdata((prevState) => [...prevState, ...DATABASE])
and try to include it on the return statement:
return (
<Importer setdata={setDatabase} />
);
not on useEffect hook.

Trying to run function only if state changes

I want to run the set totals function only if the hour's state has changed. It is running every time the component mounts instead of only if the value changes. The this.state is apart of a context file that is extremely large so I only pasted the function being used
context.js (Class Component)
set Total
if (this.state.hours > 0) {
this.setState((prevState) => {
if (prevState.hours !== this.state.hours) {
console.log(prevState.hours);
}
return {
total: this.state.total + this.state.hours * ratePerHour * Math.PI,
};
});
console.log(this.state.total, '+', this.state.hours, '*', ratePerHour);
}
This is my component tha
import React, { useState, useEffect, useContext,useRef } from 'react';
import { ProductContext } from '../pages/oniContext';
import { Container,Badge } from 'reactstrap';
import {
Subtitle,
Description,
Titlespan2,
} from '../components/common/title/index';
import { total } from '../components/total';
export const FinalQuote = () => {
const pCR = useContext(ProductContext);
const prevCountRef = useRef();
useEffect(() => {
alert('Run')
console.log(pCR.hours, 'Final Quote Run', pCR.total);
pCR.setTotal();
console.error(pCR.hours);
}, [pCR.hours]);
return (
<section className="testimonial-wrapper gradient-color" id="testimonial">
<Container>
<div className="main-title-wrapper">
<Subtitle Class="site-subtitle gradient-color" Name="Your Quote" />
<Titlespan2
Class="sitemain-subtitle"
Name={`$${Math.round(pCR.total)}`}
/>
<Description
Class="site-dec"
Name="The Shown Price is only an estimate and may increase or decrease based on demand and extent of work"
/>
{pCR.activeAddOns.map((service, index) => (
<Badge color="info" pill>
{service.title}
</Badge>
))}
</div>
</Container>
</section>
);
};
You can achieve this by using componentDidUpdate life cycle function in your component class. As per the docs
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Means, whenever the state of the component will change, the componentDidUpdate code block will be called. So we can place an if condition in the block to compare the new state with the previous state, can calculate total and recommit it to the state. Code 👇
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
hours: 0,
total: 0,
ratePerHour: 10
};
}
componentDidUpdate(prevProps, prevState) {
if (prevState.hours !== this.state.hours) {
// Calculate total
this.setState({
total: this.state.total + this.state.hours * this.state.ratePerHour * Math.PI
}
}
}
render() {
return <AnotherComponent />;
}
}
Plus it is important to note that (ref: docs)
You may call setState() immediately in componentDidUpdate() but note that it must be wrapped in a condition like in the example above, or you’ll cause an infinite loop.
In case of any other query, please feel free to reach out in the comments.
It's been a minute since I've worked with newer React features but when I can I use useEffect in my functional components. The second parameter is the the variable you want to watch for changes. If you don't supply a second parameter it'll run similar to componentDidMount and componentDidUpdate. An example of possible use:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [test, setTest] = useState('');
// Specify to watch count because we have more than one state variable
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Here's some of their documentation: https://reactjs.org/docs/hooks-effect.html
In my case, even when I added the second argument to useEffect which is an array of dependencies. It is running every time the component mounts instead of only if the value changes and I guess this is because I initialized my state variable like so
const[myStateVariable, setMyStateVariable] = React.useState('');
so I had to make this condition in my useEffect function
React.useEffect(() => {
if(myStateVariable !==''){
getMyData()
}
}, [myStateVariable]);

Trigger Child component method when prop changes

I'm trying to make a child functional component to update when a Parent component changes a value in its state that I'm passing to a this child component as a prop.
The child component "receives" the value correctly and displays the prop value, but the method does not run again.
Child component
import React from 'react'
const MyCustomTable = props => {
const {
data = [],
} = props
const finalData = getSalesData() //This is the method i want to run when the selectedMonth prop updates
const getSalesData = () => {
//It does some calculations with the prop called data
}
return (
<Box>
{JSON.stringify(props.selectedMonth.value)}
<Table
data={finalData}
/>
</Box>
)
}
SalesByFamilyBU.propTypes = {}
export default MyCustomTable
The JSON.stringify line displays the changes correctly but I guess the getSalesData() is not automatically executed.
While you could use some lifecycle method or the useEffect hook to achieve what you want to do, I would rather use a functional approach.
In your example, finalData is a derived value of props.data and props.selectedMonth. You could then compute finalData directly from these props:
const MyCustomTable = props => {
const {
data = [],
} = props;
const filterData = (data, selectedMonth) => data.map(dataPoint => ({
...dataPoint,
selected: dataPoint.month === selectedMonth,
}); // or whatever, use your function logic here
const finalData = filterData(data, props.selectedMonth.value);
return (...);
};
If you really needed to call a function each time data is changing (ex. to fetch data elsewhere) you could use something like that:
const MyComponent = ({ data }) => {
const [finalData, setFinalData] = useState([]);
const myFunction = () => {
const newData = ... // whatever you need to do
setFinalData(newData);
};
useEffect(myFunction, [data]);
return ...;
};

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"
}
}

ReactJS lifecycle method inside a function Component

Instead of writing my components inside a class, I'd like to use the function syntax.
How do I override componentDidMount, componentWillMount inside function components?
Is it even possible?
const grid = (props) => {
console.log(props);
let {skuRules} = props;
const componentDidMount = () => {
if(!props.fetched) {
props.fetchRules();
}
console.log('mount it!');
};
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
Edit: With the introduction of Hooks it is possible to implement a lifecycle kind of behavior as well as the state in the functional Components. Currently
Hooks are a new feature proposal that lets you use state and other
React features without writing a class. They are released in React as a part of v16.8.0
useEffect hook can be used to replicate lifecycle behavior, and useState can be used to store state in a function component.
Basic syntax:
useEffect(callbackFunction, [dependentProps]) => cleanupFunction
You can implement your use case in hooks like
const grid = (props) => {
console.log(props);
let {skuRules} = props;
useEffect(() => {
if(!props.fetched) {
props.fetchRules();
}
console.log('mount it!');
}, []); // passing an empty array as second argument triggers the callback in useEffect only after the initial render thus replicating `componentDidMount` lifecycle behaviour
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
useEffect can also return a function that will be run when the component is unmounted. This can be used to unsubscribe to listeners, replicating the behavior of componentWillUnmount:
Eg: componentWillUnmount
useEffect(() => {
window.addEventListener('unhandledRejection', handler);
return () => {
window.removeEventListener('unhandledRejection', handler);
}
}, [])
To make useEffect conditional on specific events, you may provide it with an array of values to check for changes:
Eg: componentDidUpdate
componentDidUpdate(prevProps, prevState) {
const { counter } = this.props;
if (this.props.counter !== prevState.counter) {
// some action here
}
}
Hooks Equivalent
useEffect(() => {
// action here
}, [props.counter]); // checks for changes in the values in this array
If you include this array, make sure to include all values from the component scope that change over time (props, state), or you may end up referencing values from previous renders.
There are some subtleties to using useEffect; check out the API Here.
Before v16.7.0
The property of function components is that they don't have access to Reacts lifecycle functions or the this keyword. You need to extend the React.Component class if you want to use the lifecycle function.
class Grid extends React.Component {
constructor(props) {
super(props)
}
componentDidMount () {
if(!this.props.fetched) {
this.props.fetchRules();
}
console.log('mount it!');
}
render() {
return(
<Content title="Promotions" breadcrumbs={breadcrumbs} fetched={skuRules.fetched}>
<Box title="Sku Promotion">
<ActionButtons buttons={actionButtons} />
<SkuRuleGrid
data={skuRules.payload}
fetch={props.fetchSkuRules}
/>
</Box>
</Content>
)
}
}
Function components are useful when you only want to render your Component without the need of extra logic.
You can use react-pure-lifecycle to add lifecycle functions to functional components.
Example:
import React, { Component } from 'react';
import lifecycle from 'react-pure-lifecycle';
const methods = {
componentDidMount(props) {
console.log('I mounted! Here are my props: ', props);
}
};
const Channels = props => (
<h1>Hello</h1>
)
export default lifecycle(methods)(Channels);
You can make your own "lifecycle methods" using hooks for maximum nostalgia.
Utility functions:
import { useEffect, useRef } from "react";
export const useComponentDidMount = handler => {
return useEffect(() => handler(), []);
};
export const useComponentDidUpdate = (handler, deps) => {
const isInitialMount = useRef(true);
useEffect(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
return handler();
}, deps);
};
export const useComponentWillUnmount = handler => {
return useEffect(() => handler, []);
};
Usage:
import {
useComponentDidMount,
useComponentDidUpdate,
useComponentWillUnmount
} from "./utils";
export const MyComponent = ({ myProp }) => {
useComponentDidMount(() => {
console.log("Component did mount!");
});
useComponentDidUpdate(() => {
console.log("Component did update!");
});
useComponentDidUpdate(() => {
console.log("myProp did update!");
}, [myProp]);
useComponentWillUnmount(() => {
console.log("Component will unmount!");
});
return <div>Hello world</div>;
};
Solution One:
You can use new react HOOKS API. Currently in React v16.8.0
Hooks let you use more of React’s features without classes.
Hooks provide a more direct API to the React concepts you already know: props, state, context, refs, and lifecycle.
Hooks solves all the problems addressed with Recompose.
A Note from the Author of recompose (acdlite, Oct 25 2018):
Hi! I created Recompose about three years ago. About a year after
that, I joined the React team. Today, we announced a proposal for
Hooks. Hooks solves all the problems I attempted to address with
Recompose three years ago, and more on top of that. I will be
discontinuing active maintenance of this package (excluding perhaps
bugfixes or patches for compatibility with future React releases), and
recommending that people use Hooks instead. Your existing code with
Recompose will still work, just don't expect any new features.
Solution Two:
If you are using react version that does not support hooks, no worries, use recompose(A React utility belt for function components and higher-order components.) instead. You can use recompose for attaching lifecycle hooks, state, handlers etc to a function component.
Here’s a render-less component that attaches lifecycle methods via the lifecycle HOC (from recompose).
// taken from https://gist.github.com/tsnieman/056af4bb9e87748c514d#file-auth-js-L33
function RenderlessComponent() {
return null;
}
export default lifecycle({
componentDidMount() {
const { checkIfAuthed } = this.props;
// Do they have an active session? ("Remember me")
checkIfAuthed();
},
componentWillReceiveProps(nextProps) {
const {
loadUser,
} = this.props;
// Various 'indicators'..
const becameAuthed = (!(this.props.auth) && nextProps.auth);
const isCurrentUser = (this.props.currentUser !== null);
if (becameAuthed) {
loadUser(nextProps.auth.uid);
}
const shouldSetCurrentUser = (!isCurrentUser && nextProps.auth);
if (shouldSetCurrentUser) {
const currentUser = nextProps.users[nextProps.auth.uid];
if (currentUser) {
this.props.setCurrentUser({
'id': nextProps.auth.uid,
...currentUser,
});
}
}
}
})(RenderlessComponent);
componentDidMount
useEffect(()=>{
// code here
})
componentWillMount
useEffect(()=>{
return ()=>{
//code here
}
})
componentDidUpdate
useEffect(()=>{
//code here
// when userName state change it will call
},[userName])
According to the documentation:
import React, { useState, useEffect } from 'react'
// Similar to componentDidMount and componentDidUpdate:
useEffect(() => {
});
see React documentation
Short and sweet answer
componentDidMount
useEffect(()=>{
// code here
})
componentWillUnmount
useEffect(()=>{
return ()=>{
//code here
}
})
componentDidUpdate
useEffect(()=>{
//code here
// when userName state change it will call
},[userName])
You can make use of create-react-class module.
Official documentation
Of course you must first install it
npm install create-react-class
Here is a working example
import React from "react";
import ReactDOM from "react-dom"
let createReactClass = require('create-react-class')
let Clock = createReactClass({
getInitialState:function(){
return {date:new Date()}
},
render:function(){
return (
<h1>{this.state.date.toLocaleTimeString()}</h1>
)
},
componentDidMount:function(){
this.timerId = setInterval(()=>this.setState({date:new Date()}),1000)
},
componentWillUnmount:function(){
clearInterval(this.timerId)
}
})
ReactDOM.render(
<Clock/>,
document.getElementById('root')
)
if you using react 16.8 you can use react Hooks...
React Hooks are functions that let you “hook into” React state and lifecycle features from function components...
docs
import React, { useState, useEffect } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const [count2, setCount2] = useState(0);
// componentDidMount
useEffect(() => {
console.log("The use effect ran");
}, []);
// // componentDidUpdate
useEffect(() => {
console.log("The use effect ran");
}, [count, count2]);
// componentWillUnmount
useEffect(() => {
console.log("The use effect ran");
return () => {
console.log("the return is being ran");
};
}, []);
useEffect(() => {
console.log(`The count has updated to ${count}`);
return () => {
console.log(`we are in the cleanup - the count is ${count}`);
};
}, [count]);
return (
<div>
<h6> Counter </h6>
<p> current count: {count} </p>
<button onClick={() => setCount(count + 1)}>increment the count</button>
<button onClick={() => setCount2(count2 + 1)}>increment count 2</button>
</div>
);
};
export default Counter;

Resources