I have a class component that looks like this:
interface MyState {
x_array: number[]
y_array: number[]
order_graph_1: number
}
class ItemsContainerOld extends React.Component<MyProps, MyState> {
constructor(props: MyProps) {
super(props)
this.state = {
x_array: [],
y_array: [],
order_graph_1: 1,
}
}
addingCoordinate(coord: {x: number; y: number}) {
const new_x_array = this.state.x_array.concat(coord.x)
const new_y_array = this.state.y_array.concat(coord.y)
this.setState({
x_array: new_x_array,
y_array: new_y_array,
})
}
setOrders(orders: number[]) {
this.setState({
order: orders[0],
})
}
render() {
return (
<div
>
<div>
<div>
<DrawerOld
addCoord={this.addCoord.bind(this)}
resetCoords={this.resetCoords.bind(this)}
/>
</div>
<div style={{paddingLeft: '70px', paddingTop: '50px'}}>
<Initial
x={this.state.x_array}
y={this.state.y_array}
deg={this.state.order_graph_1}
color={this.color_graph_1}
/>
</div>
</div>
</div>
)
}
}
export default ItemsContainerOld
I changed it to a functional component. However, upon using the functional component, within this component:
<Initial x={x_array} y={y_array}
I start getting errors like
TypeError: Cannot read property 'length' of undefined
How can I ensure that the correct values reach that component? Here's a codesandbox:
https://codesandbox.io/s/infallible-thunder-xsbrm?file=/src/ItemsContainerNew.tsx
you can always check if the value is undefined or not, like :
if(this.props.x.length===undefined){
return;
}
and if you want, you can use some default value there.
**this is the approach, every time you encounter this you have to put some condition as a workaround or set an initial default value.
Here is my sandbox: https://codesandbox.io/s/vigilant-perlman-rzwge?file=/src/Initial.tsx:932-973
and react lifecycle you should know:
Updated code, completely working : https://codesandbox.io/s/goofy-microservice-mw5j6?file=/src/Initial.tsx
You can fix the problem by changing
if (props.x.length === 0 && props.y.length === 0)
to
if (!props.x && !props.y)
Although this may cover up a deeper problem. It would be best to figure out why props.x or props.y is undefined.
You need to improve your state initialization.
Try not setting values as type 'any', be more precise, when initializing your x_arrayand y_array you could use
// Notice that not only the type has changed from "any" to "number[]",
// But the initial value is now an empty array, causing their initial values
// (and types) to be arrays since their starting point.
// When initialization happens without a starting value like "useState()",
// The state receives the value "undefined" which causes the error.
const [x_array, setXarray] = useState<number[]>([]);
const [y_array, setYattay] = useState<number[]>([]);
Related
I am trying to pass an object from Parent to Child as described in this article.
I am new to React JS and am probably missing something simple here.
The goal of this is to have a select list of "Industries" that passes the selected industry to a select list of "Categories". The selected Category would then populate a table.
I am getting the error that this.props.selectedIndustry doesn't exist on compile.
Here is my code:
class CategoryFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
industrySelect: this.props.selectedIndustry
}
console.log(this.props.selectedIndustry)
}
render() {
const { selectedCategory } = this.state;
const categories = utmcategories.filter(function(i) {return i.industry==this.props.selectedIndustry})
return (
<Select options={categories} onChange={this.handleCategoryChange}
/>
);
}
class IndustryFilter extends React.Component {
state = {
selectedIndustry: null,
};
handleIndustryChange = (selectedIndustry) => {
this.setState({ selectedIndustry });
console.log(`Option selected:`, selectedIndustry);
}
render() {
const { selectedIndustry } = this.state;
const industries = utmindustries;
return (
<div>
<Select options={industries}
onChange={this.handleIndustryChange}
autoFocus={true}
/>
<CategoryFilter selectedIndustry={selectedIndustry}/>
</div>
);
}
}
I modified the code to narrow down the issue and made my CategoryFilter this:
function CategoryFilter(props) {
return (
<div>
{props.filteredIndustry}
</div>
)
}
But still got the error:
"Objects are not valid as a React Child."
You are initialising selectedIndustry to null here:
state = {
selectedIndustry: null,
};
and then passing it in as a prop to <CategoryFilter>
I am getting the error that this.props.selectedIndustry doesn't exist
on compile. Here is my code
Not quite sure that the 'error' you are getting is actually an error. Seems like it may be the expected behaviour. Could you please show us the error message exactly?
I would expect this.props.selectedIndustry to be defined as null after <CatergoryFilter> has mounted. You should try initialising it to a value e.g. "Default Industry" and see if that console.log(this.props.selectedIndustry) in the constructor is printing "Default Industry". If not, I would suspect that the this context has not been initialised until after the constructor has returned. Try logging this value in the render() method instead.
I am new to react and getting confused between react hooks. There are many similar questions asked and I tried a few answers but it didn't work. I am trying to use a value of flag which has been set in componentDidmount() in render(). But I am getting undefined. Here is my code. Can someone help me?
export default class Shop extends Component {
constructor(props) {
super(props);
this.state = {
isContentTypeShop1: false,
};
}
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.isContentTypeShop1 = true;
}
console.log(this.isContentTypeShop1); // returns true
}
render() {
console.log(this.isContentTypeShop1); //returns undefined
return (
<ul className="progress-bar">
<li>
{(this.isContentTypeShop1) && ( // hence doesn't work
<span>
Shop 1
</span>
)}
</li>
</ul>
);
}
}
You need to make use of setState to trigger a re-render from componentDidMount. Also isContentTypeShop1 isn't a class variable but its a state
async componentDidMount() {
const basketContextData = await fetchBasketContext(); // returns json object
const basketContentType = basketContextData.basketContentType; //returns string 'shop1'
console.log(basketContentType)
if(basketContentType === 'shop1') {
this.setState({isContentTypeShop1: true});
}
}
render() {
// use it from state
console.log(this.state.isContentTypeShop1);
}
this.isContentTypeShop1 doesn't exist because isContentTypeShop1 is inside state. Try this instead:
console.log(this.state.isContentTypeShop1);
And to update isContentTypeShop1, you need to call setState:
this.setState({ isContentTypeShop1: true });
You need to use this.state.isContentTypeShop1 instead of this.isContentTypeShop1 & you can't set state using =
You need to use setState like this.setState({ isContentTypeShop1: true })
You need to read Using State Correctly part from the React docs
And for some additional reading :)
for some reason that i dont understand, i cant seem to fetch a state value in my renderer, at first i thought it was a scoping issue, but even after changing to var, my variable is undefined.
constructor(props) {
super(props);
this.state = {
stuff: {}
};
componentDidMount(){
this.getatuff.then( (result) =>{
this.setState({
stuff: result.items[0]
});
});
console.log('REACT Coveo component DOM loaded');
}
render() {
var ReactMarkdown = require('react-markdown');
var project = this.state.stuff;
debugger;
if(!Object.entries(project).length === 0){
var asd = project.body[1].value; <---UNDEFINED
return (
<div className="block">
<ReactMarkdown source={asd} />
</div>
);
}
why is my Object array value undefined in the renderer?
Note: both const variables in the screenshot were changed to var, and the behavior persists.
You'd need to define state inside your class, or inside your constructor():
constructor(props) {
super(props);
this.state = {
project: // something
}
}
componentDidMount() {
// etc.
It's not clear on your example what you're trying to achieve, but it could be one of these reasons:
on your componentDidUpdate you have a typo in this.getatuff (I assume you're trying to say getStuff
project seem to be defined on your renderer, your screenshot shows that it has a key id:5 and some others, but it might not have a key body or body might not be an array, or the array might contain only 1 value [0] instead of two values [0] and [1]. I suggest you to debug the structure of project to get the right value
You got syntax error here: if(!Object.entries(project).length === 0) it should be if (!(Object.entries(project).length === 0))
render() {
var ReactMarkdown = require('react-markdown');
var project = this.state.stuff;
debugger;
if (!(Object.entries(project).length === 0)) {
var asd = project.body[1].value; <---UNDEFINED
return (
<div className="block">
<ReactMarkdown source={asd} />
</div>
);
}
I'm starting to learn it but can't find a solution for this -> I have an input and when the value exceeds 20 characters I want a tooltip to appear with the full value as is typed. I have it all built and kind of working. Problem is that I get a maximum call stack error because the state is being changed on every key press - I'm not sure of the best/correct way to go about it... any help would be greatly appreciated
See my code below and here is the pen
console.clear();
class Input extends React.Component {
render(){
return(
<div>
<input
className="main-input"
onChange={this.props.onChange}
placeholder={"Tell me something"}
/>
</div>
)
};
}
class Tooltip extends React.Component {
render(){
return(
<div
className="tooltip"
data-status={this.props.isShowing}>
<p>{this.props.TooltipValue}</p>
</div>
)
}
}
class App extends React.Component {
constructor(){
super();
this.state = {
message: '',
isShowing: false
}
}
onChange(e) {
this.setState({
message: e.target.value
});
}
render(){
const inputVal = (this.state.message.length);
if(inputVal >= 20){
this.setState({isShowing: true})
}
// else {
// this.setState({isShowing: false})
// }
return(
<div className="container">
<Tooltip
TooltipValue={this.state.message}
isShowing={this.state.isShowing}
/>
<Input onChange={this.onChange.bind(this)}/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('Main')
)
Why maximum call stack error when using setState in render function ?
Because when we do setState, React trigger the re-rendering of the component, if you do setstate inside render then after setState, component will render again, again it will find setState again render, it will become a infinite loop, So never do any setState inside render method.
Check this answer for more details about setState behaviour.
Instead of putting that check inside render method, put that inside onChange method, like this:
onChange(e) {
const inputVal = e.target.value;
this.setState({
message: inputVal,
isShowing : inputVal.length > 20 ? true : false
});
}
And remove these lines:
if(inputVal >= 20){
this.setState({isShowing: true})
}
// else {
// this.setState({isShowing: false})
// }
Or since the showing of Tooltip depends on the value of input element, you can avoid extra variable and directly check the length of input element with Tooltip property isShowing, like this:
<Tooltip
TooltipValue={this.state.message}
isShowing={this.state.message.length >= 20}
/>
No need to use a separate state for the tooltip since it already depends on the state value you can do it like this
<Tooltip
TooltipValue={this.state.message}
isShowing={this.state.message.length > 20} >
Also since you are using setState in the render() you are getting an error because setState triggers a re-render and thus beginning an infine loop as soon as you if-condition becomes true.
CODEPEN
It seems like in some cases React will not propagate state changes into child components.
Minimum example
I tried to boil down my observed behavior into the smallest test case I could find, but it still ended up being pretty large. Sorry about that.
class Inner extends React.Component<{ value: number }, { value: number }> {
constructor(props: { value: number }) {
super(props);
this.state = { value: props.value };
}
render() {
return <span> { this.state.value } </span>
}
}
class Test extends React.Component<{}, { value: number }> {
constructor(props: {}) {
super(props);
this.state = { value: 0 };
setTimeout(() => { this.setState({ value: 1 }); }, 500);
}
render() {
return <span>
<Inner value={ this.state.value } />
</span>;
}
}
React.render(<Test />, document.getElementById('somediv'));
Quick summary
Test has "value" in it's state, which is displayed passed into and displayed by it's inner component, "Inner".
Inside Test's constructor method, I set value on state to 0. After 500ms, I then set it to 1. I'd expect this to change Inner's display to also show a 1, since it depends on value from this.state, but it doesn't. Why not?
The very weird thing is that if I change the line return <span> { this.state.value } </span> to use this.props.value inside Inner, then it actually does update. Of course, I need to use state in my app that I distilled this example from.
I expect I am missing some fundamental part of React here. What is it?
The constructor for Inner is only run once when the component instance is first created.
As a result, this is only run once, not every time the props change:
this.state = { value: props.value };
So if you pass new props into Inner, the value of this.props.value will change, but the value of this.state.value won't.
Right now this is what happens:
The timeout calls setValue on Test and updates the state of Test
React re-renders Test
As part of the rendering process Test passes a new prop value into Inner
Inner re-renders - its props have changed, but its state hasn't.