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);
Related
As an exercise, I wanted to create a useEffect, useState functionality from React.
I achieved similar functionality but I don't like this part of the code:
function Comp() {
let store = new Store();
let [a, setA] = useState(10, store);
let [b, setB] = useState(15, store);
Basically, each time I create a new "component" I need to create a store. Global store doesn't work because I would need to send additional data when setting new state value
( setValue(value, "KEY") ).
Is there a design pattern or something that I could use to achieve what React does? The only thing that comes to mind is writting sort of a transpiler, but it seems like an overkill and like I am missing something.
Link to the sandbox:
https://codesandbox.io/s/sweet-artem-wi2imw?file=/src/store.js
EDIT:
The question is how to avoid passing the store to each newly created component.
EDIT 2:
Functionality I want to achieve:
I create a "component"
Make use of the useState hook to get [ getter, setter ] =
useState(value)
Each time a setter is called, the useEffect
inside that component should be called, or more precisely it should
check with store if it should be called.
There would be numerous
components, calling setValue(value) from component A should not
trigger a callback effect in component B (why global store doesn't
work, unless i provide additional arguments on a setter to let the
store know which callbacks it should run
If you are sure not to write a transpiler yourself, try decorator pattern, but it only saves your time on new Store, you still need send it manually:
// something like this:
#withNewStore
function Comp(store) {
let [a, setA] = useState(10, store);
let [b, setB] = useState(15, store);
However, if you are sure you only need useState and useEffect, you can do it more wildy:
// something like this:
#withHooks
function Comp({useState,useEffect)) {
let [a, setA] = useState(10);
let [b, setB] = useState(15);
But keep in mind that the decorator spec is still on stage2 and will be likely to be changed in the future. It's not recommanded to use it in production.
More on this decorators in typescript
I opted for the following solution, per #bbbbbbbboat suggestion
function decorate(fn) {
let store = new Store();
let props = {
useEffect: useEffectBuilder(store),
useState: useStateBuilder(store)
};
return fn(props);
}
function App() {
decorate(Comp);
decorate(Comp2);
}
function useStateBuilder(store) {
return (value) => {
return useState(value, store);
};
}
function useEffectBuilder(store) {
return (f, deps) => {
return useEffect(store, f, deps);
};
}
Usage:
function Comp({useState,useEffect)) {
let [a, setA] = useState(10);
let [b, setB] = useState(15);
according to many examples, this should work:
const [_timeseries, $timeseries] = useState({hi:'lol'})
useEffect(() => {
socket.on('plot', e => {
let keyname = Object.keys(e)[0]
$timeseries({..._timeseries, [keyname] : value)})
}
}, [])
console.log(_timeseries) // here results in the initial state, not the set state
The first time it merges, it works.
But once a new event with another keyname enters, it replaces the whole thing again.
Instead of adding a new key with [keyname], the old [keyname] is being replaced.
The problem here is closures.
The callback assigned to the useEffect closes the initial value of _timeseries in it's the lexical scope and it never updated.
To fix it, you need to use the functional useState which uses the most updated state within its callback:
const [_timeseries, $timeseries] = useState({hi:'lol'})
useEffect(() => {
socket.on('plot', e => {
let keyname = Object.keys(e)[0]
$timeseries(timeSeries => {...timeseries, [keyname] : value)})
}
}, [])
The useState hook gives you a function which replaces the state entirely with a new value (doesn't merge it): https://reactjs.org/docs/hooks-state.html
However, unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
You can use setState with a function and merge it yourself:
$timeseries((old) => ({...old, [keyname] : value)}))
If you use it without a function it might have the old values (because you don't specify it as a dependency of useEffect)
I'm trying to avoid using conditional statements with React Hooks, but I've reached a point where I may need some advanced techniques. I have here 3 hooks that depend on each other. Each hook returns a tuple [bool, object] that the next hook requires to execute.
Internally, each hook is an asynchronous operation and I don't have access to modify them.
const [loadingA, resultA] = useHook1();
const [loadingB, resultB] = useHook2(resultA.prop1); // Error!
const [loadingC, resultC] = useHook3(resultB.prop2); // Error!
if (loadingA || loadingB || loadingC) {
return <span>loading...</span>;
}
// All results are required for this component.
return <MyComponent x={resultA.x} y={resultB.y} z={resultC.z} />;
The above breaks because useHook2 and useHook3 require the arguments to be defined. Unfortunately, I can't use a condition like this:
// this is not allowed
if (!loadingA) {
const [loadingB, resultB] = useHook2(resultA.prop1);
}
Does anyone have any tips on how to stagger hooks such that they execute based on the result of a previous asynchronous hook?
Return some default value in hook2 and hook3 if the parameter is undefined. If you can't modify hook2 or hook3 directly, you can always wrap them in another hook (useHook2Wrapper, useHook3Wrapper) and do the undefined value validation.
const [loadingA, resultA] = useHook1();
const [loadingB, resultB] = useHook2(resultA && resultA.prop1);
const [loadingC, resultC] = useHook3(resultB && resultB.prop2);
const useHook2 = (someProp) => {
if (!someProp) {
return [false, undefined];
}
// rest of the hook logic
}
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);
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;