How to avoid repeatedly instantiating an object - javascript

I'm using reactjs and there is a instantiation in componentDidUpdate(). But when update state, the componentDidUpdate() will be execute and the Object will repeat statement
ps: const barrage = new Barrage(); the object will be execute repeatly
componentDidUpdate() {
const barrage = new Barrage();
}
const barrage = new Barrage();
how to avoid the Object statement repeatly execute

you haven't really explained what you want to achieve but I will try to solve this specific question by saying you can put it in a state if you need it to create new instance only once
constructor(props) {
super(props);
...
this.state = {
barrage : null
}
...
}
componentDidUpdate(prevState) {
if(!prevState.barrage) {
this.setState({ barrage : new Barrage() )}
}
}

You could create it in componentDidMount and cleanup in componentWillUnmount
class TestComponent extends React.Component {
barage = null;
componentDidMount() {
this.barage = new Barage();
}
componentWillUnmount() {
this.barage = null;
}
}
It's really hard to guess what you're doing with that object without any code. I can only assume that it is independent of the updates to the component, if any.

Related

How to map a redux state slice to a local component state?

Currently I'm using the following:
const mapStateToProps = state => {
return {
stateInProps: state.someSliceIWant
};
};
Foo.propTypes = {
dispatch: PropTypes.func,
stateInProps: PropTypes.object
};
export default connect(mapStateToProps)(Foo);
and since we can only actually map state to props, and not directly to state in my component, I'm getting around this in a hacky way by doing this:
class Foo extends React.Component {
constructor(props){
super(props);
this.state = {};
setTimeout(() => {this.setState(this.props.stateInProps);},1);
}
How should I do this properly? I've tried using different lifecycle hooks and all sorts of methods.
An interesting thing I found was that if I put setState() in mapStateToProps.... it ALMOST works.
const mapStateToProps = state => {
Foo.setState(state.someSliceIWant);
return {
stateInProps: state.someSliceIWant
};
};
It throws a bunch of errors, but when I delete that line and webpack grinds through and the page re-renders, the state has actually been updated properly and the values are there! WHAT?! I can't figure out how to get it do that, without throwing the errors, as Redux/React itself won't allow it as best I can tell.
Have you tried ComponentWillReceiveProps after mapping state to Props?
For instance:
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
The hacky way you are using, you might not able to access your stateInProps if it get updated in later lifecycle.
You can use something as below
class Foo extends React.Component {
state = {};
componentWillMount() { this.setStateInProps(this.props); }
componentWillReceiveProps(nextProps) { this.setStateInProps(nextProps)}
setStateInProps= (props) => this.setState(props.stateInProps)
}

React - Setting component state using a function outside of state, is it wrong?

Is it wrong to use setState in a function outside of the React component?
Example:
// myFunction.js
function myFunction() {
...
this.setState({ ... })
}
// App.js
import myFunction from './myFunction
class App extends Component {
constructor() {
super()
this.myFunction = myFunction.bind(this)
}
...
}
I'm not sure the way you're binding will actually work. You could do something like:
export const getName = (klass) => {
klass.setState({ name: 'Colin'})
}
then
class App extends Component {
state = {
name: 'React'
};
handleClick = () => {
getName(this);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.handleClick}>change name</button>
</div>
);
}
}
Working example here.
So the only reasons to do this is if you are reducing repeated code, e.g. two components use the same logic before calling this.setState, or if you want to make testing easier by having a separate pure function to test. For this reason I recommend not calling this.setState in your outside function, but rather returning the object you need so it can you can call this.setState on it.
function calculateSomeState(data) {
// ...
return { updated: data };
}
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = calculateSomeState(props.data);
}
handleChange = (e) => {
const value = e.target.value;
this.setState(calculateSomeState({ ...props.data, value }));
}
}
It looks like a bug waiting to happen... If you want to use an external function to set state, you can use the alternative syntax provided by React:
this.setState((prevState, props) => {
return updatedState; //can be a partial state, like in the regular setState
});
That callback can easily be extracted to an external function and it's guaranteed to work
It is not wrong, the function is never called outside the component. This is a mix-in technique. bind isn't needed, as long as the function isn't used as a callback. In this case myFunction is same among all instances, a more efficient way would be:
class App extends Component {}
App.prototype.myFunction = myFunction;

React. Cannot start function inside the getDerivedStateFromProps

I cannot understand, why when I try to start the function getTodosList inside the getDerivedStateFromProps the method - it always retrun to me the TypeError - Cannot read property 'getTodosList' of null.
Also after I start use the getDerivedStateFromProps - my function does not start in componentDidMount too...
What is I'm doing wrong? (
import React, { Component } from 'react';
import {Doughnut} from 'react-chartjs-2';
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
// some state...
}
getTodosList = (todos) => {
console.log(todos);
const all = [];
const done = [];
// some logic...
}
componentDidMount() {
const { todos } = this.props.state.iteams;
console.log('componentDidMount', todos);
this.getTodosList(todos);
}
static getDerivedStateFromProps(nextProps, prevState) {
const { todos } = nextProps.state.iteams;
console.log(this.getTodosList, prevState.datasets, 'componentWillReceiveProps', todos);
this.getTodosList(todos); // TypeError: Cannot read property 'getTodosList' of null
}
getDerivedStateFromProps is a static property of the class (as indicated by the static keyword in front of it). This means it doesn't have access to any instance functions/properties.
Declare your getTodosList as static as well (if it also doesn't use any instance properties) then call Chart.getTodosList(todos).
Edit:
If you call setState in getTodosList, you can change it to return the state object instead, then you can construct/update your state based on the returned object from your calling function.
Eg.
static getTodosList = todos => {
...
return { someState: someData }; //instead of this.setState({ someState });
}
static getDerivedStateFromProps() {
...
return Chart.getTodosList(todos);
}
You also don't need the componentDidMount if it's doing the same thing as getDerivedStateFromProps.
getDerivedStateFromProps is a static method, it will not have the access of any instance property/method, means this will not be available inside that method.
Use componentDidUpdate to perform the operation you want on state/props change.
You need to put the check between prevState/prevProps vs the new values.
componentDidUpdate(prevProps, prevState, snapshot) {
// compare this.state and prevState or compare the props values
// and perform the operation
}

Access an object from componentDidMount()

I called the following library in componentDidMount() and it returns an object successfully.
componentDidMount() {
var objData =call.MyLibStart('12121', this);
}
Now I need to use objData in render() section. I also need to access some attribute of the objData such as, objData.
render() {
//I need the object.postate here
}
How can I access an object there? Is using a the state a good idea here?
You can access the object just like #3Dos's answer. If you want to modify the value of objData, then use it as a state. If you only want to render that object or get the value to check for something then a class property is enough.
Make sure you're getting the object the right way:
componentWillMount () {
this.objData = Object.assign({}, call.MyLibStart('12121', this))
console.log('objData: ', this.objData) // inspect the object in debugger to check it's keys
}
The reason with componentDidMount is it only run after the render function. Your app's flow goes like this:
in constructor(): this.objData = null
in render(): this.objData = null
in componentDidMount(): this.objData = some object
At this time render function will not be updated because it will only update if you have made some changes to your state. Since this.objData is not a state so it will always be null in render. So by changing componentDidMount to componentWillMount your objData won't be null when render got called.
What you want is probably to set an instance variable in a constructor so you can access it in your other class methods like for exemple :
class MyComponent extends Component {
constructor (props) {
super(props)
this.objData = call.MyLibStart('12121', this)
}
render () {
// Do whatever you like with this.objData
return null
}
}
Unless you need access to mounted components, you could set it as initial state in the constructor:
constructor(props) {
super(props)
this.state = {
objData: call.MyLibStart('12121', this)
}
}
render() {
// use this.state.objData here
}

Clearing state es6 React

I am trying to clear a components state but can't find a reference for the es6 syntax. I was using:
this.replaceState(this.getInitialState());
however this does not work with the es6 class syntax.
How would I achieve the same result?
To the best of my knowledge, React components don't keep a copy of the initial state, so you'll just have to do it yourself.
const initialState = {
/* etc */
};
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = initialState;
}
reset() {
this.setState(initialState);
}
/* etc */
}
Beware that the line this.state = initialState; requires you never to mutate your state, otherwise you'll pollute initialState and make a reset impossible. If you can't avoid mutations, then you'll need to create a copy of initialState in the constructor. (Or make initialState a function, as per getInitialState().)
Finally, I'd recommend you use setState() and not replaceState().
Problem
The accepted answer:
const initialState = {
/* etc */
};
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = initialState;
}
reset() {
this.setState(initialState);
}
/* etc */
}
unfortunately is not correct.
initialState is passed as a reference to this.state, so whenever you change state you also change initialState (const doesn't really matter here). The result is that you can never go back to initialState.
Solution
You have to deep copy initialState to state, then it will work. Either write a deep copy function yourself or use some existing module like this.
This is the solution implemented as a function:
Class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = this.getInitialState();
}
getInitialState = () => ({
/* state props */
})
resetState = () => {
this.setState(this.getInitialState());
}
}
The solutions that involve setting this.state directly aren't working for me in React 16, so here is what I did to reset each key:
const initialState = { example: 'example' }
...
constructor() {
super()
this.state = initialState
}
...
reset() {
const keys = Object.keys(this.state)
const stateReset = keys.reduce((acc, v) => ({ ...acc, [v]: undefined }), {})
this.setState({ ...stateReset, ...initialState })
}
First, you'll need to store your initial state when using the componentWillMount() function from the component lifecycle:
componentWillMount() {
this.initialState = this.state
}
This stores your initial state and can be used to reset the state whenever you need by calling
this.setState(this.initialState)
const initialState = {
a: '',
b: '',
c: ''
};
class ExampleComponent extends Component {
state = { ...initialState } // use spread operator to avoid mutation
handleReset = this.handleReset.bind(this);
handleReset() {
this.setState(initialState);
}
}
Remember that in order to be able to reset the state it is important not to mutate initialState.
state = {...initialState} // GOOD
// => state points to a new obj in memory which has the values of initialState
state = initialState // BAD
// => they point to the same obj in memory
The most convenient way would be to use ES6 Spread Operator. But you could also use Object.assign instead. They would both achieve the same.
state = Object.assign({}, initialState); // GOOD
state = {...initialState}; // GOOD
In most cases you dont need a deep copy, rarely initial state is object of objects, so using spread operator which babel transpiles to the object.assign should be fine.
So, inside constructor you would have:
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
key: value,
key2: value
}
this.initialState = { ...this.state }
}
}
From there you can use
this.setState(this.initialState);
to reset. But if for some reason your initial state is more complex object, use some library.
This is how I handle it:
class MyComponent extends React.Component{
constructor(props){
super(props);
this._initState = {
a: 1,
b: 2
}
this.state = this._initState;
}
_resetState(){
this.setState(this._initState);
}
}
Update:
Actually this is wrong. For future readers please refer to #RaptoX answer.
Also, you can use an Immutable library in order to prevent weird state modification caused by reference assignation.
I will add to the above answer that the reset function should also assign state like so:
reset() {
this.state = initialState;
this.setState(initialState);
}
The reason being that if your state picks up a property that wasn't in the initial state, that key/value won't be cleared out, as setState just updates existing keys. Assignment is not enough to get the component to re-render, so include the setState call as well -- you could even get away with this.setState({}) after the assignment.
In some circumstances, it's sufficient to just set all values of state to null.
If you're state is updated in such a way, that you don't know what might be in there, you might want to use
this.setState(Object.assign(...Object.keys(this.state).map(k => ({[k]: null}))))
Which will change the state as follows
{foo: 1, bar: 2, spam: "whatever"} > {foo: null, bar: null, spam: null}
Not a solution in all cases, but works well for me.
You can clone your state object, loop through each one, and set it to undefined. This method is not as good as the accepted answer, though.
const clonedState = this.state;
const keys = Object.keys(clonedState);
keys.forEach(key => (clonedState[key] = undefined));
this.setState(clonedState);
class MyComponent extends Component {
constructor(props){
super(props)
this.state = {
inputVal: props.inputValue
}
// preserve the initial state in a new object
this.baseState = this.state
}
resetForm = () => {
this.setState(this.baseState)
}
}
use deep copy, you can do it with lodash:
import _ from "lodash";
const INITIAL_STATE = {};
constructor(props) {
super(props);
this.state = _.cloneDeep(INITIAL_STATE);
}
reset() {
this.setState(_.cloneDeep(INITIAL_STATE));
}
class x extends Components {
constructor() {
super();
this.state = {
name: 'mark',
age: 32,
isAdmin: true,
hits: 0,
// since this.state is an object
// simply add a method..
resetSelectively() {
//i don't want to reset both name and age
// THIS IS FOR TRANSPARENCY. You don't need to code for name and age
// it will assume the values in default..
// this.name = this.name; //which means the current state.
// this.age = this.age;
// do reset isAdmin and hits(suppose this.state.hits is 100 now)
isAdmin = false;
hits = 0;
}// resetSelectively..
}//constructor..
/* now from any function i can just call */
myfunction() {
/**
* this function code..
*/
resetValues();
}// myfunction..
resetValues() {
this.state.resetSelectively();
}//resetValues
/////
//finally you can reset the values in constructor selectively at any point
...rest of the class..
}//class

Categories

Resources