I'd like to know how I can better store the variables I am using for moment in my react component.
I've already tried just declaring the variables in the constructor below the setState, but these variables are not changeable. I then tried to store the variables in the state, as key value pairs but since one of the values is dependent on another, it did not work.
Here is my current constructor:
constructor(props) {
super(props);
this.state = {
scooter: props.scooter,
header: props.header,
reservation: ""
};
}
componentWillReceiveProps(nextProps) {
this.setState({
scooter: nextProps.scooter,
header: nextProps.header
});
}
Some items will have to be called as well in componentsWillRecieveProps as the item will change with next props.
variables below
render() {
const reservation = this.state.scooter.state.LastAdminReservation;
var a = moment();
const c = moment(reservation.PendingDate);
const d = moment(reservation.EndDate);
const duration = d.from(c, true);
var timeSince = a.from(d, true);
const date = moment(reservation.DownloadAckDate).format("DD.MM.YYYY
HH:mm");
I'd like to be able to call these variables in my return and have var a and var timeSince be updated on next props while not storing them in my render as this is bad practice. Thanks!
You can do like this (putting in the state only variable that will be used inside other methods.
constructor() {
super();
const a = moment();
const c = moment(new Date());
const d = moment(new Date());
const duration = d.from(c, true);
const timeSince = a.from(d, true);
this.state = {
name: 'React',
timeSince
};
}
Try your best not to derive state from props. However, if you really need to, getDerivedStateFromProps is a safer method introduced in React 16.
componentWillReceiveProps is a deprecated method that you should be avoiding.
Instead, you can create a function that will return the date you need, and call it from your render method. If you are worried with performance, you can memoize that function. Example here
If you are using a state management solution like Redux, you can also move that logic up to a selector and pass down only the data you need into your component.
Related
So let's assume I have this code
import {useState} from 'react'
class Something{
counter:number
constructor(counter:number){
this.counter = counter
}
}
function functionalComponent(){
const [state, setState] = useState<Something>(new Something(5))
//other codes .....
function changeState(){
//see below
}
return (
<>
<h1>{state.counter}</h1>
<button onClick={changeState}>Increment</button>
</>
)
}
export default functionalComponent
Now at some point, I want to change the state to have counter increment 1, the below code works but...
function changeState(){
state.counter += 1
setState(state)
}
And before you all start shouting in the comments, I know that we shouldn't mutate the state directly and instead create a new state and pass it on to the setState. But the thing is for js objects and arrays we can simply use the spread operators, but I couldn't find anything simpler for class objects.
So my question is "What should be the best approach for situations like this?"
One I could think of and is using
function changeState(){
let newState = new Something(state.counter+1)
setState(newState)
}
But it becomes counter productive if my object contains many fields say 15
You can use your class inside an object and use the spread operator:
const [state, setState] = useState({state:new Something(5)})
state.state.counter = 1
setState({...state})
One Approach that I tried and seems to be close enough is to have a clone function in the class that returns a clone(new Instance). See the below example for a better understanding
class Something{
counter:number
constructor(counter:number){
this.counter = counter
}
clone(){
return new Something(this.counter)
}
}
So this way whenever I'll call clone it will return a new Instance.
Now in the changeState function, we can do
function changeState(){
let newState = state.clone()
newState.counter += 1
setState(newState)
}
These steps ensures that no where the data is mutated as adheres to the principal of React.
While it would be technically possible to create a new Something every time you want to change the state:
const [state, setState] = useState(5)
const something = useMemo(() => new Something(state), [state]);
I'd also first strongly consider whether you need a class inside a functional component at all - it's pretty odd. There's probably a more intuitive solution.
Another option which may or may not work depending on what your actual code contains in the class constructor would be to have the state contain an object for all instance properties:
const [state, setState] = useState({ counter: 5 });
const something = useMemo(() => Object.assign(new Something(), state), [state]);
Lets just use useState and functional components it would make your life way easier..
I am calling an event on react-native-voice to recognize the speech and calling an API and setting the state. But when I am setting a new value to a state array the array only have the new value instead of having the previous value. This is my code
constructor(props){
super(props);
this.state = {
results = [];
}
Voice.onSpeechResults = this.onSpeechResults;
}
and I have a event which is been triggered to call an API like this.
onSpeechResults = e => {
this._startRecognizing();
//var joined = this.state.results.concat();
getProductListingService(e.value[0]).then(data=>{
let demoVar = Object.values(data);
var newArray = this.state.results;
newArray.push(demoVar[1]);
this.setState({
results:newArray
})
this.setState({
show:true
});
});
};
But when setting the state I am only able to get the new element been pushed into the array instead of the previous state. I have tried using concat, spread, and setting a new array. Stuck, Please help. :(
First declare your state like:
this.state = {
results: []; // Don't use = for properties
}
second notice that if you setState() with same values that it already has, it doesn't set the state(and call render function).
I am trying to re-create the 'useState' hook from React as a silly fun personal exercise, but am encountering trouble when accessing variables from my 'global state'. I know that this has no practical use outside of react, but I just thought it would be something to attempt regardless.
Currently I have the following implementation below, but because the destructured variable is set only that first time and is not updated when using the corresponding setter, it always will return the same variable. I fully understand why this is happening but I am unsure if there is a way to get this working at all or if this is a lost cause. The destructured setter does update the global state, but the variable is of course as previously mentioned not accessing the global state again since it is only set that initial time.
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = prop => {
const id = PROP_ID++;
GLOBAL_STATE[id] = prop;
return [
(() => {
return GLOBAL_STATE[id];
})(),
function(nv) {
GLOBAL_STATE[id] = nv;
}
];
};
const [userName, setUserName] = useState("Chris");
const [favCol, setFavCol] = useState("red");
console.log(GLOBAL_STATE);
console.log(userName);
setUserName("Bob");
console.dir(GLOBAL_STATE);
console.log(userName);
All I want to know if there is a way to set the destructured reference variable equal to some sort of function that will always be called to get the new variable from the global state when that variable is referenced.
I think you're missing a piece of the puzzle here.
React hooks depend on the position of their call within a given functional component. Without the encapsulating function, you remove the usefulness of the state being provided by the hook, because they're only being called once in your example, thus the reference in the destructuring syntax never gets updated as you observed.
Let's get them working in the context of functions.
const { component, useState } = (function () {
const functions = new WeakMap()
const stack = []
let hooks
let index
function component (fn) {
return function (...args) {
try {
stack.push({ hooks, index })
hooks = functions.get(fn)
index = 0
if (!hooks) {
functions.set(fn, hooks = [])
}
return fn.apply(this, args)
} finally {
({ hooks, index } = stack.pop())
}
}
}
function useState (initialValue) {
const hook = index++
if (hook === hooks.length) {
hooks.push(initialValue)
}
return [
hooks[hook],
function setState (action) {
if (typeof action === 'function') {
hooks[hook] = action(hooks[hook])
} else {
hooks[hook] = action
}
}
]
}
return { component, useState }
})()
const fibonacci = component(function () {
const [a, setA] = useState(1)
const [b, setB] = useState(1)
setA(b)
setB(a + b)
return a
})
const sequence = component(function () {
const [text, setText] = useState('')
setText(
text.length === 0
? fibonacci().toString()
: [text, fibonacci()].join()
)
return text
})
for (let i = 0; i < 20; i++) {
console.log(sequence())
}
The stack variable here allows us to nest our stateful function calls, and the hooks variable keeps track of the existing hook states by position within the currently executing component of the stack.
This implementation might seem overly-complicated, but the point of component() and stack is to partially mimic how the React framework treats functional components. This is still much simpler than how React works, because we're treating all calls of the same function as if it's the same instance of a functional component.
On the other hand, in React, a particular function could be used for several different instances, distinguishable from each other based on a number of factors such as the position in the hierarchy of the virtual DOM, the key and ref props, etc., so it's much more complicated than this.
It occurs to me you just want to get your example working. For that, all you need to do is change your variable to a getter function:
const useState = state => [
() => state,
value => { state = value }
];
const [getUserName, setUserName] = useState('Chris');
const [getFavCol, setFavCol] = useState('red');
console.log(getUserName());
setUserName('Bob');
console.log(getUserName());
Much simpler than what you had and doesn't require any globals to work.
If the manual getter seems too inconvenient, then you can't destructure, but you can implement an approach that's almost as easy to use:
const useState = state => ({
get state () { return state },
set (value) { state = value }
});
const userName = useState('Chris');
const favCol = useState('red');
console.log(userName.state);
userName.set('Bob');
console.log(userName.state);
This is a very interesting question.
The short answer is: no, that’s not possible*.
The long answer
The long answer is how JavaScript handles primitives and objects. Primitives values are copied during assignment (userName here: const [userName, setUserName] = useState("Chris"); which is a string), while in case of object a reference would be copied.
In order to play with it, I came with something like that (mind you, that is not solution to your challenge, rather explanation to my answer):
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = prop => {
const id = PROP_ID++;
GLOBAL_STATE[id] = {
_value: prop,
[Symbol.toPrimitive]() { return this._value },
}
const tuple = [
GLOBAL_STATE[id],
(nv) => GLOBAL_STATE[id]._value = nv,
];
return tuple;
};
const [userName, setUserName] = useState("Chris");
const [favCol, setFavCol] = useState("red");
console.log(GLOBAL_STATE);
console.log(userName);
console.log('set user name to:', setUserName("Bob"));
console.dir(GLOBAL_STATE);
console.log('' + userName);
GLOBAL_STATE entry is now object, so when you destructure it after calling useState only a reference is changed. Then update changes data inside this object but what we assigned in the first place is still there.
I added Symbo.toPrimitive property which coerses object to a primitive value but sadly, this will not work on it’s own. Only when run as '' + userName. Which means it behaves differently than you expected. At this point I stopped experimenting.
React
I went to Facebook’s Github and tried to trace what they are doing but gave up due to imports of imports of imports. Hence, I will take an educated guess here, based on Hooks API behaviour. I think that your implementation is rather faithful to the original. We use useState in a function and the value doesn’t change there. Only when state is changed and then the component is re-rendered with a new value, which again is assigned and won’t change.
*I will gladly welcome anyone who proves this notion wrong.
How about something along the following lines...
let PROP_ID = 0;
let GLOBAL_STATE = {};
const useState = (varName, prop) => {
const id = PROP_ID++;
GLOBAL_STATE[id] = prop;
Object.defineProperty(window, varName, {
get: function(){
return GLOBAL_STATE[id];
}
});
return ((nv) => {
GLOBAL_STATE[id] = nv;
});
};
const setUserName = useState("userName", "Chris");
const setFavCol = useState("favCol", "red");
console.log(GLOBAL_STATE);
console.log(userName);
setUserName("Bob");
console.dir(GLOBAL_STATE);
console.log(userName);
Note that I've changed your interface a bit such that you have to pass the name of the variable to the useState function. Seems a bit kludgey, but allows a getter to be configured, in this case, following your example, on the global scope (ie, "window"), which might not be the best practice.
There is a solution, but only if you're okay using a dirty, dirty hack.
The following approach uses a with statement and a Proxy containing a custom get handler, and requires object destructuring syntax in order to determine the variable name from the property key of the setter function:
// initialize useState() hook with independent scope
function createHook (scope = Object.create(null)) {
const setter = /^set([A-Z][^\W_]*)$/;
function useState (initialValue) {
// return proxy from useState() so that object destructuring syntax
// can be used to get variable name and initialize setter function
return new Proxy(scope, {
get (target, propertyKey) {
if (!setter.test(propertyKey)) {
throw new TypeError(`Invalid setter name '${propertyKey}'`);
}
// get variable name from property key of setter function
const [, name] = propertyKey.match(setter);
const key = name[0].toLowerCase() + name.slice(1);
// support updater callback
const setState = value => {
target[key] = (
typeof value === 'function'
? value(target[key])
: value
);
};
// initialize state
setState(initialValue);
// return setter as property value
return setState;
}
});
}
return { scope, useState };
}
// example usage with a little magic
{
const { scope, useState } = createHook();
const { setFoo } = useState('bar');
console.log(scope.foo);
setFoo(42);
console.log(scope.foo);
}
// example use with more magic
const { scope, useState } = createHook();
with (scope) {
const { setUserName } = useState('Chris');
const { setFavCol } = useState('red');
console.log(userName, favCol);
setUserName('Bob');
setFavCol(color => `dark${color}`);
console.log(userName, favCol);
}
The following usage ends up being very similar to Jon Trent's answer by abusing implicit globals:
function createHook(e=Object.create(null)){var t=/^set([A-Z][^\W_]*)$/;return{scope:e,useState:n=>new Proxy(e,{get(e,r){if(!t.test(r))throw new TypeError(`Invalid setter name '${r}'`);var[,o]=r.match(t),c=o[0].toLowerCase()+o.slice(1),s=t=>{e[c]="function"==typeof t?t(e[c]):t};return s(n),s}})}}
const { useState } = createHook(window);
const { setUserName } = useState('Chris');
const { setFavCol } = useState('red');
console.log(userName, favCol);
setUserName('Bob');
setFavCol(color => `dark${color}`);
console.log(userName, favCol);
I encountered error in react wherein this is undefined. This is my first time developing a react application.
In UI, it says Unhandled Rejection (TypeError): Cannot read property 'setState' of undefined while in console the value of this is undefined.
Thank you for your help.
Here is the existing code:
import React, { useState, useEffect, useRef } from "react";
//import makeData from "../makeData";
import { useTableState } from "react-table";
import { Button } from "../Styles";
import Table from "../TransactionPanelTable";
// Simulate a server
const getServerData = async ({ filters, sortBy, pageSize, pageIndex }) => {
await new Promise(resolve => setTimeout(resolve, 500));
// Ideally, you would pass this info to the server, but we'll do it here for convenience
const filtersArr = Object.entries(filters);
// Get our base data
let rows = [];
for (let i = 0; i < 1000; i++) {
rows.push({
transaction_seq: 1234,
rec_count: 1234,
user_id: "test",
updated_at: "",
duration: 1.23
});
}
// Apply Filters
if (filtersArr.length) {
rows = rows.filter(row =>
filtersArr.every(([key, value]) => row[key].includes(value))
);
}
// Apply Sorting
if (sortBy.length) {
const [{ id, desc }] = sortBy;
rows = [...rows].sort(
(a, b) => (a[id] > b[id] ? 1 : a[id] === b[id] ? 0 : -1) * (desc ? -1 : 1)
);
}
// Get page counts
const pageCount = Math.ceil(rows.length / pageSize);
const rowStart = pageSize * pageIndex;
const rowEnd = rowStart + pageSize;
// Get the current page
rows = rows.slice(rowStart, rowEnd);
let checkedMap = new Map();
rows.forEach(row => checkedMap.set(row, false)); //preset each to false, ideally w/ a key instead of the entire row
this.setState({ checkedMap: checkedMap });
//handleCheckedChange(row) {
// let modifiedMap = this.state.checkedMap;
// modifiedMap.set(row, !this.state.checkedMap.get(row));
// this.setState({ checkedMap: modifiedMap });
//}
return {
rows,
pageCount
};
};
'this' is undefined because you haven't bounded the context of the component to the method.
As you are new to react, i would suggest you to go through concepts like bind,es6 and other js lingos so that you can code better and avoid such errors.
In this case you need to bind the context either by using bind method or by using es6 arrow functions which are class functions.
Add below code in constructor function where you are calling this getServerData function.
getServerData = getServerData.bind(this);
Here bind will return new method which will set context (this) to your calling class.
It is preferable to set state in class with in that promise resolved function.
First, let's answer your question. this is undefined because you are using ES6 modules and you are using an arrow function const getServerData = async (...) => {.
Inside arrow functions, the this binding is not dynamic, but is instead lexical. That means that this actually refers to the originating context (outside your arrow function).
In a basic js script, that would point to the Window global context. But you are using ES6 module, with import statements. One of the goals of ES6 modules is to isolate code. So inside such a module, there is no default access to a global Window context, so your this end up being undefined.
Now, several misconceptions are recognizable in your code.
writing an ES6 modules, you should have an export statement somewhere inside in order to consume it outside.
The request to a server should preferably be isolated in another function, so that the structure of your component will be more easy to apprehend, especially if you are learning React.
Read the very good React doc to understand the basics of it.
You should not use async when defining a React functional component
Since you have no state defined in your component (importing useState is not enough, you must use it: State Hooks doc), you cannot use setState.
this.setState refer to using React class syntax to define your component, useState refer to using State Hooks. You cannot do both in a same component. You have to choose.
So to sum-up, your getServerData function should be outside of your Component and should not have to do anything with state management. And you should build a React component that will call your function in a lifecycle method.
Stay confident, you will get it sooner than you think! ;-)
this.getServerData = this.getServerData.bind(this);
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
In the above example whenever setCount(count + 1) is invoked a re-render happens. I am curious to learn the flow.
I tried looking into the source code. I could not find any reference of useState or other hooks at github.com/facebook/react.
I installed react#next via npm i react#next and found the following at node_modules/react/cjs/react.development.js
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
On tracing back for dispatcher.useState(), I could only find the following ...
function resolveDispatcher() {
var dispatcher = ReactCurrentOwner.currentDispatcher;
!(dispatcher !== null) ? invariant(false, 'Hooks can only be called inside the body of a function component.') : void 0;
return dispatcher;
}
var ReactCurrentOwner = {
/**
* #internal
* #type {ReactComponent}
*/
current: null,
currentDispatcher: null
};
I wonder where can I find dispatcher.useState() implementation and learn how it triggers re-render when setState setCount is invoked.
Any pointer would be helpful.
Thanks!
The key in understanding this is the following paragraph from the Hooks FAQ
How does React associate Hook calls with components?
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.
(This also explains the Rules of Hooks. Hooks need to be called unconditionally in the same order, otherwise the association of memory cell and hook is messed up.)
Let's walk through your counter example, and see what happens. For simplicity I will refer to the compiled development React source code and React DOM source code, both version 16.13.1.
The example starts when the component mounts and useState() (defined on line 1581) is called for the first time.
function useState(initialState) {
var dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
As you have noticed, this calls resolveDispatcher() (defined on line 1546). The dispatcher refers internally to the component that's currently being rendered. Within a component you can (if you dare to get fired), have a look at the dispatcher, e.g. via
console.log(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current)
If you apply this in case of the counter example, you will notice that the dispatcher.useState() refers to the react-dom code. When the component is first mounted, useState refers to the one defined on line 15986 which calls mountState(). Upon re-rendering, the dispatcher has changed and the function useState() on line 16077 is triggered, which calls updateState(). Both methods, mountState() on line 15352 and updateState() on line 15371, return the count, setCount pair.
Tracing ReactCurrentDispatcher gets quite messy. However, the fact of its existence is already enough to understand how the re-rendering happens. The magic happens behind the scene. As the FAQ states, React keeps track of the currently rendered component. This means, useState() knows which component it is attached to, how to find the state information and how to trigger the re-rendering.
setState is a method on the Component/PureComponent class, so it will do whatever is implemented in the Component class (including calling the render method).
setState offloads the state update to enqueueSetState so the fact that it's bound to this is really only a consequence of using classes and extending from Component. Once, you realize that the state update isn't actually being handled by the component itself and the this is just a convenient way to access the state update functionality, then useState not being explicitly bound to your component makes much more sense.
I also tried to understand the logic behind useState in a very simplified and basic manner, if we just look into its basic functionalities, excluding optimizations and async behavior, then we found that it is basically doing 4 things in common,
maintaining of State, primary work to do
re-rendering of the component through which it get called so that caller component can get the latest value for state
as it caused the re-rendering of the caller component it means it must maintain the instance or context of that component too, which also allows us to use useState for multiple component at once.
as we are free to use as many useState as we want inside our component that means it must maintain some identity for each useState inside the same component.
keeping these things in mind I come up with the below snippet
const Demo = (function React() {
let workInProgress = false;
let context = null;
const internalRendering = (callingContext) => {
context = callingContext;
context();
};
const intialRender = (component) => {
context = component;
workInProgress = true;
context.state = [];
context.TotalcallerId = -1; // to store the count of total number of useState within a component
context.count = -1; // counter to keep track of useStates within component
internalRendering(context);
workInProgress = false;
context.TotalcallerId = context.count;
context = null;
};
const useState = (initState) => {
if (!context) throw new Error("Can only be called inside function");
// resetting the count so that it can maintain the order of useState being called
context.count =
context.count === context.TotalcallerId ? -1 : context.count;
let callId = ++context.count;
// will only initialize the value of setState on initial render
const setState =
!workInProgress ||
(() => {
const instanceCallerId = callId;
const memoizedContext = context;
return (updatedState) => {
memoizedContext.state[instanceCallerId].value = updatedState;
internalRendering(memoizedContext);
};
})();
context.state[callId] = context.state[callId] || {
value: initState,
setValue: setState,
};
return [context.state[callId].value, context.state[callId].setValue];
};
return { useState, intialRender };
})();
const { useState, intialRender } = Demo;
const Component = () => {
const [count, setCount] = useState(1);
const [greeting, setGreeting] = useState("hello");
const changeCount = () => setCount(100);
const changeGreeting = () => setGreeting("hi");
setTimeout(() => {
changeCount();
changeGreeting();
}, 5000);
return console.log(`count ${count} name ${greeting}`);
};
const anotherComponent = () => {
const [count, setCount] = useState(50);
const [value, setValue] = useState("World");
const changeCount = () => setCount(500);
const changeValue = () => setValue("React");
setTimeout(() => {
changeCount();
changeValue();
}, 10000);
return console.log(`count ${count} name ${value}`);
};
intialRender(Component);
intialRender(anotherComponent);
here useState and initialRender are taken from Demo. intialRender is use to call the components initially, it will initialize the context first and then on that context set the state as an empty array (there are multiple useState on each component so we need array to maintain it) and also we need counter to make count for each useState, and TotalCounter to store total number of useState being called for each component.
FunctionComponent is different. In the past, they are pure, simple. But now they have their own state.
It's easy to forget that react use createElement wrap all the JSX node, also includes FunctionComponent.
function FunctionComponent(){
return <div>123</div>;
}
const a=<FunctionComponent/>
//after babel transform
function FunctionComponent() {
return React.createElement("div", null, "123");
}
var a = React.createElement(FunctionComponent, null);
The FunctionComponent was passed to react. When setState is called, it's easy to re-render;