can anyone tell me how to transfer it to form acceptable in class component? Thanks
const [isAuth, setIsAuth] = useState(localStorage.getItem("isAuth"));
Something like the following
define the state in the constructor and initialize it with the value you use in the useState
create a setIsAuth method that sets the state to the new value passed
class YourComponent extends React.Component{
constructor(props){
super(props);
this.state = {
isAuth: localStorage.getItem("isAuth")
}
}
setIsAuth = (newValue) => {
this.setState({
isAuth: newValue
});
}
render() {
...
}
}
Related
I have a react class based component where I have defined a state as follows:
class MyReactClass extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDataPoints: new Set()
};
}
// This method is called dynamically when there is new addition of data
storeData = (metricName, dataPoint) => {
if (this.state.selectedDataPoints.has(dataPoint)) {
this.state.selectedDataPoints.delete(dataPoint);
} else {
this.state.selectedDataPoints.add(dataPoint);
}
};
render () {
return (
<p>{this.state.selectedDataPoints}</p>
);
}
}
Note that initially, the state is an empty set, nothing is displayed.
But when the state gets populated eventually, I am facing trouble in spinning up the variable again. It is always taking as the original state which is an empty set.
If you want the component to re-render, you have to call this.setState () - function.
You can use componentshouldUpdate method to let your state reflect and should set the state using this.state({}) method.
Use this code to set state for a set:
export default class Checklist extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedDataPoints: new Set()
}
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
}
addItem(item) {
this.setState(({ selectedDataPoints }) => ({
selectedDataPoints: new Set(selectedDataPoints).add(item)
}));
}
removeItem(item) {
this.setState(({ selectedDataPoints }) => {
const newSelectedDataPoints = new Set(selectedDataPoints);
newSelectedDataPoints.delete(item);
return {
selectedDataPoints: newSelectedDataPoints
};
});
}
getItemCheckedStatus(item) {
return this.state.checkedItems.has(item);
}
// This method is called dynamically when there is new addition of data
storeData = (metricName, dataPoint) => {
if (this.state.selectedDataPoints.has(dataPoint)) {
this.state.selectedDataPoints.removeItem(dataPoint);
} else {
this.state.selectedDataPoints.addItem(dataPoint);
}
};
render () {
return (
<p>{this.state.selectedDataPoints}</p>
);
}
}
Having the below jsx code:
import React, { Component } from 'react';
import RemoteAssets from '../modules/RemoteAssets';
class RemoteOptions extends Component {
constructor(props) {
super(props);
this.state = {
RemoteOptions: []
}
}
componentDidMount() {
const { api, locale } = this.props;
RemoteAssets.loadRemoteOptions({ api, locale }).then((RemoteOptions) => {
console.log( 'RemoteOptions', RemoteOptions);
this.setState((state, props) => ({
RemoteOptions
}), () => {
this.render()
});
})
}
render() {
return (
<div className="row">
<div className="col-4">
<label >Opt: </label>
</div>
<div className=" col-8">
{JSON.stringify(this.state.RemoteOptions)}
</div>
</div>
);
}
}
export default RemoteOptions;
This is what happens to me:
componentDidMount logs correctly the payload expected.
console.log( 'RemoteOptions', RemoteOptions);
So I believe that It will also set State as expected:
this.setState((state, props) => ({
RemoteOptions
}), () => {
this.render()
});
I also added above a this.render() stmt to be sure the component will be re-rendered after updating the state.
But :
{JSON.stringify(this.state.RemoteOptions)}
Will always return "[]" as the init state before componentDidMount happens and update the state.
How should I arrange this component to have my render update the with the payĆ²oad loaded async?
Name conflict
Your state name and class name are in conflict.
class RemoteOptions extends Component { // class name
constructor(props) {
super(props);
this.state = {
RemoteOptions: [] // state name
}
}
...
Call your state something different.
Why not simply using setState the way documentation suggests?
this.setState({ RemoteOptions });
Render method will be automatically called right after the state is set.
I implemented the skeleton of the problem, and everything works as expected.
const loadRemoteOptions = () => new Promise(resolve => {
setTimeout(() => resolve('myRemoteOptions'), 1000)
})
class App extends React.Component {
state = {
remoteOptions: null
}
componentDidMount(){
loadRemoteOptions().then(remoteOptions => this.setState({ remoteOptions }))
}
render(){
return this.state.remoteOptions || 'Loading...';
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
From this blog article, the rendering of a component can be altered this way:
function iiHOC(WrappedComponent) {
return class Enhancer extends WrappedComponent {
render() {
const elementsTree = super.render()
let newProps = {};
if (elementsTree && elementsTree.type === 'input') {
newProps = {value: 'may the force be with you'}
}
const props = Object.assign({}, elementsTree.props, newProps)
const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children)
return newElementsTree
}
}
}
This seems to work only if the passed components is itself a class component.
How would one go about writing the same code so that it works on functional components ?
I believe you can just pass the props from the wrapper directly to the functional component like so:
const jediEnhancer = (FunctionalComponentToWrap) => {
return class jediEnhancer extends React.Component {
constructor(props){
super(props);
this.state = {
forceIsWithUser: false
};
this.awakenTheForce = this.awakenTheForce.bind(this);
awakenTheForce(){
this.setState({forceIsWithUser: true});
}
render(){
return <FunctionalComponentToWrap awakenTheForce={this.awakenTheForce} {...this.props} />
}
}
}
I have a class, ElementBuilder below, and when the user saves the Element they've built, I want the state to reset to the values below.
I have some functions in this class that I haven't provided but that change the state of title, size, and color.
In ES 5, I would have a getInitialState function on my class and could call this.getInitialState() in a function.
This element lives in my app for the lifecycle of a logged in user and I want the default values to always be the same regardless of past usage.
How do I achieve this without writing a function that sets an object of default values (or maybe that's the answer)? thanks!
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = {
title: 'Testing,
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState({ this.getInitialState() });
}
}
You may use a getter function:
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = this.initialState;
}
get initialState() {
return {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState(this.initialState);
}
}
or just a variable:
constructor(props) {
super(props);
this.initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
this.state = this.initialState;
}
Using the proposed class fields, you could do something like this:
class ElementBuilder extends Component {
static initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce'
}
constructor(props) {
super(props)
this.state = ElementBuilder.initialState
}
resetBuilder() {
this.setState(ElementBuilder.initialState)
}
}
Since the initial state doesn't seem to depend on anything instance specific, just define the value outside the class:
const initialState = {...};
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = initialState;
}
resetBuilder() {
this.setState(initialState);
}
}
Use an High Order Component to clear component state (rerender)
Exemple Element.jsx :
// Target ----- //
class Element extends Component {
constructor(props){
super(props)
const {
initState = {}
} = props
this.state = {initState}
}
render() {
return (
<div className="element-x">
{...}
</div>
)
}
}
// Target Manager ----- //
class ElementMgr extends Component {
constructor(props){
super(props)
const {
hash = 0
} = props
this.state = {
hash, // hash is a post.id
load: false
}
}
render() {
const {load} = this.state
if (load) {
return (<div className="element-x"/>)
} else {
return (<Element {...this.props}/>)
}
}
componentWillReceiveProps(nextProps) {
const {hash = 0} = nextProps
if (hash !== this.state.hash) {
this.setState({load:true})
setTimeout(() => this.setState({
hash,
load:false
}),0)
}
}
}
export default ElementMgr
I have problem with automatically re-rendering view, when state is changed.
State has been changed, but render() is not called. But when I call this.forceUpdate(), everything is ok, but I think that's not the best solution.
Can someone help me with that ?
class TODOItems extends React.Component {
constructor() {
super();
this.loadItems();
}
loadItems() {
this.state = {
todos: Store.getItems()
};
}
componentDidMount(){
//this loads new items to this.state.todos, but render() is not called
Store.addChangeListener(() => { this.loadItems(); this.forceUpdate(); });
}
componentWillUnmount(){
Store.removeChangeListener(() => { this.loadItems(); });
}
render() {
console.log("data changed, re-render");
//...
}}
You should be using this.state = {}; (like in your loadItems() method) from the constructor when you are declaring the initial state. When you want to update the items, use this.setState({}). For example:
constructor() {
super();
this.state = {
todos: Store.getItems()
};
}
reloadItems() {
this.setState({
todos: Store.getItems()
});
}
and update your componentDidMount:
Store.addChangeListener(() => { this.reloadItems(); });
You sholdn't mutate this.state directly. You should use this.setState method.
Change loadItems:
loadItems() {
this.setState({
todos: Store.getItems()
});
}
More in react docs
In your component, whenever you directly manipulate state you need to use the following:
this.setState({});
Complete code:
class TODOItems extends React.Component {
constructor() {
super();
this.loadItems();
}
loadItems() {
let newState = Store.getItems();
this.setState = {
todos: newState
};
}
componentDidMount(){
//this loads new items to this.state.todos, but render() is not called
Store.addChangeListener(() => { this.loadItems(); this.forceUpdate(); });
}
componentWillUnmount(){
Store.removeChangeListener(() => { this.loadItems(); });
}
render() {
console.log("data changed, re-render");
//...
}}