I have this on reactjs in a functional component:
const searchTitle = useRef();
<FormGroup className={'filterItems p-3 active'} ref={searchTitle}>
<div onClick={()=> handleTitleToggle()}>
</div>
</FormGroup>
And want to toggle class on FormGroup via click on div.
const handleTitleToggle = () => {
searchTitle.current.classList.toggle('active')
}
But give me error:
TypeError: Cannot read properties of null (reading 'classList')
But if I use ref on div, it works fine, any idea?
<div ref={searchTitle} onClick={()=> handleTitleToggle()}>
</div>
if you are using MUI FormGroup Component be sure that it accept ref
and when you have chain of props on you object use optional chaining to avoid errors like this
searchTitle?.current?.classList?.toggle('active')
Related
I'm a newbie to React.js and I face with a strange error that I can't figure out why that's happening, I searched but and didn't find any solutions.
Here is is my component codes:
import React, { useState } from "react";
const AddMovie = () => {
const [movie, setMovie] = useState({ title: "", director: "" });
const onChange = (value, filed) => {
setMovie({
[filed]: value,
});
};
const onSubmit = (e) => {
e.preventDefault();
console.log(movie.title, movie.director);
setMovie({ title: "", director: "" });
};
return (
<div>
<form className="flex -mx-2" onSubmit={onSubmit}>
<div className="ml-2">
<input
type="text"
className="border p-2 rounded-md focus:outline-none"
placeholder="Movie Title"
value={movie.title}
onChange={(e) => onChange(e.target.value, "title")}
required
/>
</div>
<div className="mx-2">
<input
type="text"
className="border p-2 rounded-md focus:outline-none"
placeholder="director"
value={movie.director}
onChange={(e) => onChange(e.target.value, "director")}
required
/>
</div>
<div>
<button
type="submit"
className="h-full border rounded-md px-4 text-white bg-gray-900"
>
Add Movie
</button>
</div>
</form>
</div>
);
};
export default AddMovie;
and the error that I face with it is this: (i referred to DOC and I read this section but it didn't work as well)
react_devtools_backend.js:3973 Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
so what is the problem?
I have done this way of binding (object syntax) in Vue.js a lot of times but I haven't been facing this once, so is a bit queer for me.
The error is due to value={movie.director} being undefined once you change your movie title. That happens because you miss use the setter API,
With hooks (READ THE NOTE), the state setter does not merge state as in class component, change it to:
// field = 'title'
setMovie(prevState => ({...prevState, [field]: value }))
Unlike the setState method found in class components, useState does not automatically merge update objects.
Unlike the setState method found in class components, useState does not automatically merge update objects.
I am receiving the following errors
Warning: memo: The first argument must be a component. Instead received: object
Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare}). If you meant to render a collection of children, use an array instead.
They happen when I change this component
const Tab = () => onLastTab
? <AccountTab data={data.account} />
: <InfoTab data={data.info} />
To be this component, the only difference is the use of React.memo
const Tab = () => onLastTab
? React.memo(<TabOne data={data.one} />)
: React.memo(<TabTwo data={data.two} />)
Those components wrapped in React.memo are definately just functional components that look like
const TabOne = ({data}) => (
<div>
<div className='d-flex '>
...
</div>
</div>
)
Why would this be happening? What can I do to stop it?
As the error message explains, you need to pass component to the React.memo(), not an object. TabOne is obviously a component name but you already created an object of that component and passed it through the React.memo().
You need fix your code as follows:
const TabOne = ({data}) => (
<div>
<div className='d-flex '>
...
</div>
</div>
)
export default React.memo(TabOne)
const Tab = () => onLastTab
? <TabOne data={data.one} />
: <TabTwo data={data.two} />
I want to bundle some data together with a component. Here is an example of a SFC that has a property called name. I do not want to use the property name with the component named MyFormTab. Instead I would like to access this property from the parent component and assign it to be displayed within the parent.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
I would then like to render this component inside a parent and use the name property for another purpose
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name='Tab1' />
const Tab2 = <MyFormTab name='Tab2' />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1.name</h2>
<h2 onClick={setCurrentTab(2)}>Tab2.name</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
Instead of an SFC, I could also use a class I'm thinking.
class MyFormTab {
constructor(name){
this.name = name
}
render(){
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='email'></input>
</div>
</>
)
}
}
My project is predominantly using hooks however. My team lead(who doesn't know React much) will probably be hesitant towards mixing class components with hooks. I've read on other posts that hooks can basically replace class components in most situations. I don't know how hooks could be better, or even be used in this situation.
What do you think would be a good way to do what I am trying to do? Is putting SFC's with hooks and class components into the same project a good idea? Am I looking at this whole thing wrong?
Thank you
In react props are passed only from parent to child. So you can just have a parent with that name value and passed it down if you want to.
Edited my answer to respond to you edit.
const MyFormTab = (props) => {
const name = props.name
return (
<>
<div className='flex-center-col'>
<input type='email'></input>
<input type='text'></input>
</div>
</>
)
}
const ParentOfMyFormTab = () => {
const [currentTab, setCurrentTab] = useState(1)
const Tab1 = <MyFormTab name=`Tab1` />
const Tab2 = <MyFormTab name=`Tab2` />
return (
<form>
<div id="tabTitles">
<h2 onClick={setCurrentTab(1)}>Tab1</h2>
<h2 onClick={setCurrentTab(2)}>Tab2</h2>
</div>
{currentTab === 1 ? <Tab1 /> : <Tab2 />}
</form>
)
}
To you question about mixing class based and function components. You can't use hooks with class based components so don't, and there is no need to. I think you should learn more about the basics of react. If you need to share data with other components, the data should be in the parent component, passed to children or in a React context.
Lets say I have a base component that uses forwardRef like so:
const BaseMessage = React.forwardRef((props, ref) => (
<div ref={ref}>
{props.icon}
<h2>{props.title}</h2>
<p>{props.message}</p>
</div>
)
Now I want to create a second component, ErrorMessage that is essentially a copy of the BaseMessage but with a predefined value for props.icon, such that the icon prop is not needed to be set. Otherwise, its an exact copy of BaseMessage.
<ErrorMessage title="Oops!" message="Something went wrong when submitting the form. Please try again." />
I don't want to have to do this, since it feels weird to have two layers of forwardRef going on here:
const ErrorMessage = React.forwardRef(({icon, ...props}, ref) => (
<BaseMessage ref={ref} icon={<svg></svg>} {...props} />
))
Is there a way I can make a clone/copy of BaseMessage without having to reimplement forwardRef for ErrorMessage as well? I know there are utils out there like withProps from recompose but I'd like to avoid using a library if I can.
Try cloneElememt
React.cloneElement(BaseMessage, { icon: '' })
I am new to Redux and currently using an API to fetch data. I am trying to pass the state from my parent to this.props.children using React.cloneElement. I think I am making a mistake when i am using React.cloneElement as the debugger is showing the state to be null when i pass it to the cloneElement function. Following is my parent render method:
render(){
const {courses} = this.state;
debugger;
let fn = (child) => {
return React.cloneElement(child, {
courses: courses
});
};
let childrenWithProps = React.Children.map(this.props.children, fn);
return (
<div>
<div className="container jumbotron jumbotron-fluid">
<h1>CoursesPage</h1>
<p>This page adds and lists all the courses</p>
<Link to="/courses/courseslist">
<Button color="primary">Course Listing</Button>
</Link>
</div>
{childrenWithProps}
</div>
);
}
From the Console, i can fairly assume it is calling the children correctly, but passing null value in the courses. However when i simply pass <CourseList courses={courses} /> it correctly assumes the state. So where am i exactly going wrong?
I get the following error in the console:
Uncaught TypeError: Cannot read property 'map' of undefined
at CourseList (courseList.js:20)
at ReactCompositeComponent.js:305
at measureLifeCyclePerf (ReactCompositeComponent.js:75)
at ReactCompositeComponentWrapper._constructComponentWithoutOwner (ReactCompositeComponent.js:304)
at ReactCompositeComponentWrapper._constructComponent (ReactCompositeComponent.js:279)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:187)
at Object.mountComponent (ReactReconciler.js:45)
at ReactDOMComponent.mountChildren (ReactMultiChild.js:236)
at ReactDOMComponent._createInitialChildren (ReactDOMComponent.js:703)
at ReactDOMComponent.mountComponent (ReactDOMComponent.js:522)
..where courseList is the child component.
Much help appreciated.
Since you're passing in a variable from the Parent class to the child class CourseList you will need to use props instead
const {courses} = this.props;
Link to Documentation Components and Props
This might be what you want instead.
render(){
const {coursesList} = this.props;
return (
<div>
<div className="container jumbotron jumbotron-fluid">
<h1>CoursesPage</h1>
<p>This page adds and lists all the courses</p>
<Link to="/courses/courseslist">
<Button color="primary">Course Listing</Button>
</Link>
</div>
{coursesList}
</div>
);
}