React Player Controls - HTML5 video event handlers not recognizing video object - html

I'm new to the React framework, so I'm still learning JSX syntax and patterns.
I am attempting to hook a custom video control UI into an HTML5 video element, but to no avail.
I can get the individual PLAY and PAUSE buttons to control the video with a simple onClick function, but when I combine PLAY/PAUSE as a toggle element with the component, I can't figure out how to combine the PLAY/PAUSE icon toggle events with my handlePlay()/handlePause() functions.
I'm sure this is a novice step that I am missing, but I am pretty much stuck here...any feedback will be much appreciated.
*EDIT: added this line inside "PlaybackControls" ( onClick={isPlaying ? console.log('PLAYING!') : console.log('PAUSED!')} )
The console.log() prints 'PLAYNING!' and 'PAUSED!' onClick event, as expected...but if I replace the console.log()s with calls to the "handlePlay()" and "handlePause()" functions...nothing happens.
What am I missing?
A sample of my code is listed below:
import React, { Component } from 'react';
import { PlaybackControls, PlayButton, PauseButton, FormattedTime,
TimeMarker, ProgressBar } from 'react-player-controls';
import customControls from './customControls.scss';
export default class Video01 extends Component {
constructor(props) {
super(props);
this.state = {
isPlayable: true,
isPlaying: false,
showPrevious: false,
hasPrevious: false,
showNext: false,
hasNext: false
}
this.handlePlay = this.handlePlay.bind(this)
this.handlePause = this.handlePause.bind(this)
}
componentDidMount() {
}
componentWillMount() {
}
/**********************************************************************\
Video Playback Controls
\**********************************************************************/
handlePlay () {
if (this.props.isPlayable) {
this.props.onPlaybackChange(true)
this.refs.video01Ref.play()
}
}
handlePause () {
this.props.onPlaybackChange(false)
this.refs.video01Ref.pause()
}
render() {
return (
<div>
<div className={styles.container} data-tid="container">
<div className={styles.videoContainer} data-tid="videoContainer">
<video ref="video01Ref" src="./video/myVideo.webm" type="video/webm" />
</div>
</div>
<div className={customControls.ControlsWrapper}>
<PlaybackControls className={customControls.PlayButton, customControls.PauseButton}
isPlayable={this.state.isPlayable}
isPlaying={this.state.isPlaying}
showPrevious={false}
hasPrevious={false}
showNext={false}
hasNext={false}
onPlaybackChange={isPlaying => this.setState(Object.assign({}, this.state, { isPlaying: isPlaying }))}
onClick={isPlaying ? console.log('PLAYING!') : console.log('PAUSED!')}
/>
<ProgressBar className={customControls.ProgressBar} />
<TimeMarker className={customControls.TimeMarker} />
</div>
</div>
);
}
}

I made a bit of progress so I decided to answer my own question, in hopes of spurring some feedback from some good Samaritan React/JSX gurus.
I am still getting familiar with the React/JSX syntax, but I am really liking what I have learned so far. The modular approach is definitely much more efficient as it relates to memory/optimization...and it makes it much easier to pin-point bugs and errors. With that being said...here's what I discovered so far:
I figured out how to play/pause my video via an external component (Custom Player Controls).
I learned that it is wise to engineer my layout with individual(nested) components, as opposed to one large mess (i.e. , and are individual components that are combined into a class, which is then inserted into my component, which is inserted into my )
What I am still trying to figure out is how to pass props between components. The concept of States and Properties makes sense to me, but I am lacking some fundamental understanding of how to properly execute a workflow. I am sure it has something to do with React Life Cycles, but that's an entirely separate conversation.
For now, an example of my updated code is posted below. I am able to Play/Pause an HTML5 video with and external components (Custom Player Controls), but how to I pass the props element back to the custom controls components? For example, how do I map the default props (i.e. "currentTime", "duration", "seeking", "ended" => to the "currentTime", "totalTime", "onSeek", etc.)?
Pardon my lengthy rant, but any feedback will be greatly appreciate. Here's my updated code:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { PlaybackControls, PlayButton, PauseButton, FormattedTime, TimeMarker, TimeMarkerType, ProgressBar } from 'react-player-controls';
import customControls from './customControls.scss';
export default class CustomControls01 extends Component {
constructor(props) {
super(props);
this.state = {
isEnabled: true,
isPlayable: true,
isPlaying: false,
showPrevious: false,
hasPrevious: false,
showNext: false,
hasNext: false,
totalTime: 28,
currentTime: 0,
bufferedTime: 0,
isSeekable: true,
lastSeekStart: 0,
lastSeekEnd: 0,
markerSeparator: ' / ',
firstMarkerType: TimeMarkerType.ELAPSED,
secondMarkerType: TimeMarkerType.DURATION,
}
this.handlePlay = this.handlePlay.bind(this)
this.handlePause = this.handlePause.bind(this)
}
componentDidMount() {
}
componentWillUnmount() {
}
/**********************************************************************\
Video Playback Controls
\**********************************************************************/
handlePlay() {
vid01.play()
}
handlePause() {
vid01.pause()
}
render() {
const { isPlayable, isPlaying, showPrevious, showNext, hasPrevious, hasNext, totalTime, currentTime, markerSeparator, firstMarkerType, secondMarkerType, bufferedTime, isSeekable, lastSeekStart, lastSeekEnd, lastIntent, className, extraClasses, childClasses, style, childrenStyles, onPlaybackChange } = this.props;
const TimeMarkerType = {
ELAPSED: 'ELAPSED',
REMAINING: 'REMAINING',
REMAINING_NEGATIVE: 'REMAINING_NEGATIVE',
DURATION: 'DURATION',
}
return (
<div className={customControls.ControlsWrapper}>
<PlaybackControls className={customControls.PlayButton, customControls.PauseButton}
isPlayable={this.state.isPlayable}
isPlaying={this.state.isPlaying}
showPrevious={false}
hasPrevious={false}
showNext={false}
hasNext={false}
onPlaybackChange={isPlaying => this.setState({ ...this.state, isPlaying }, isPlaying ? (vid01) => this.handlePlay(isPlaying, isPlayable) : (vid01) => this.handlePause(isPlaying, isPlayable))}
/>
<ProgressBar className={customControls.ProgressBar}
totalTime={this.state.totalTime}
currentTime={this.state.currentTime}
bufferedTime={this.state.bufferedTime}
isSeekable={this.state.isSeekable}
onSeek={time => this.setState((vid01) => ({ currentTime: time }))}
onSeekStart={time => this.setState((vid01) => ({ lastSeekStart: time }))}
onSeekEnd={time => this.setState((vid01) => ({ lastSeekEnd: time }))}
onIntent={time => this.setState((vid01) => ({ lastIntent: time }))}
/>
<TimeMarker className={customControls.TimeMarker}
totalTime={this.state.totalTime}
currentTime={this.state.currentTime}
markerSeparator={this.state.markerSeparator}
firstMarkerType={this.state.firstMarkerType}
secondMarkerType={this.state.secondMarkerType}
/>
</div>
);
}
}
CustomControls01.propTypes = {
};

Related

React App crashes sometimes when opening react-signature-canvas

Sorry, I can't be very specific with the details of the problem as it only happens sometimes, and I haven't been able to recreate it, which means I have no clue where to start trying to fix it.
It appears to only happen on really cheap android tablets.
I have a page with a form where the user fills in details, The problem happens just after they have entered their name into a text field and then once they press onto the react-signature-canvas to start drawing their signature the app crashes (doesn't crash all the time).
in the past, I think the crash was caused when the keyboard was still open when the user tried to start drawing on the signature pad.
As I said, I'm finding it really difficult to fix as I can't recreate it, so any help at all would be greatly appreciated.
I'm using React Hooks and Formik.
Form:
<h2>Guardian Full Name</h2>
<MyTextField
label="Guardian Full Name"
name="parentName"
required
/>
<ErrorMessage
component={"div"}
className={"termsConditionText error"}
name={"parentSignature"}
/>
<SignaturePad setFieldValue={setFieldValue} />
SignaturePad:
import React, { useRef, useState } from "react";
import { Button } from "semantic-ui-react";
import "../../pages/SignDisclaimerForm/SignDisclaimerForm.css";
import "./signaturePad.css";
import SignatureCanvas from "react-signature-canvas";
export const SignaturePad = props => {
const [canvasImageUrl, setCanvasImageUrl] = useState([
props.parentSignature || ""
]);
let sigCanvas = useRef();
const clearCanvas = () => sigCanvas.current.clear();
const saveCanvas = async () => {
if (sigCanvas.current.isEmpty()) return;
document.getElementById("parentName").blur();
props.setFieldValue(
"parentSignature",
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
setCanvasImageUrl(
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
};
return (
<div>
{!props.disabled && (
<div>
<h2 style={{ marginLeft: "5%" }}>Guardian Signature</h2>
<div className={"sigContainer"}>
<SignatureCanvas
ref={sigCanvas}
canvasProps={{ className: "sigPad" }}
onEnd={saveCanvas}
/>
</div>
<Button
style={{ marginLeft: "5%", marginTop: "2%", marginRight: "2%" }}
type={"button"}
onClick={clearCanvas}
children={"Clear"}
/>
<br />
<br />
</div>
)}
{canvasImageUrl[0] && (
<div className={"signatureDisplay"}>
<img
src={canvasImageUrl}
alt={"Guardian Signature"}
style={{ height: "100%", width: "100%" }}
/>
</div>
)}
</div>
);
};
Sentry issue report also below.
Issue Title:
TypeError HTMLCanvasElement.r(src/helpers)
error
Cannot read property 'push' of undefined
Issue Body:
../../src/helpers.ts in HTMLCanvasElement.r at line 85:17
}
// Attempt to invoke user-land function
// NOTE: If you are a Sentry user, and you are seeing this stack frame, it
// means the sentry.javascript SDK caught an error invoking your application code. This
// is expected behavior and NOT indicative of a bug with sentry.javascript.
return fn.apply(this, wrappedArguments);
// tslint:enable:no-unsafe-any
} catch (ex) {
ignoreNextOnError();
withScope((scope: Scope) => {
Bread Crumbs:
This is what the form looks like:
Formik author here...
You might be setting state from an unmounted DOM element (the canvas). It doesn't happen all the time because it's a race condition. You should check whether the canvas ref is actually mounted before using methods on it within your callbacks.
// ...
const sigCanvas = useRef(null);
const clearCanvas = () => {
if (sigCanvas.current != null) {
sigCanvas.current.clear();
}
};
const saveCanvas = async () => {
// Ensure that the canvas is mounted before using it
if (sigCanvas.current != null) {
if (sigCanvas.current.isEmpty()) return;
document.getElementById("parentName").blur();
props.setFieldValue(
"parentSignature",
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
setCanvasImageUrl(
sigCanvas.current.getTrimmedCanvas().toDataURL("image/png")
);
}
};
// ...
Thank you to everyone who helped me, I really appreciated it.
What I did in the end to fix the problem was just to have a green button the user had to press in order to open the signature pad.
The fact that the user has to press the open button, gives the keyboard enough time to completely dismiss before the user starts to draw on the signature pad.
Thank you :)

Using a image path in a JSON object to dynamically render image in component

I am running into an issue that seems to be "similar" to other issues dealing with a JSON object in React, though I can not seem how to translate these answers. I am creating a small game inside of React (not React Native). I am able to call my JSON object and pull text, though, when it comes to pulling an image path, it will not render. To get a basic idea of this game (that was in HTML/ JS) take a look at this link for the overall functionality.
Here is the Issue, I have a dynamically rendering set of objects based on state in the parent Component (GameLogic.js). I am then passing state down to the great-great-grandchild elements where it will render a two photos. These photo paths are stored in a local JSON file (I can read strings from the characters.json file in a console.log at this level). Though it can read the path (via console.log), it is not rendering these images. I am how ever able to render these images as long as I am not stringing together a long dynamic path.
Here is the file structure:
-components folder
|-GameLogic.js (parent element that handles the render)
|-Bandersnatch.js (child element)
|-NewComponents folder
|-ImageContainer.js (grandChild element)
|-ImageSquare.js (great grandChild element)
-images folder
|-snatch_images folder (yes... I know how bad this sounds...)
|-escape_snatch.png
|-The rest of the images (there are about 20)
-characters.json
-App.js
JSON example: I need the file path at Array[0].scene[0].choiceOneImg
[
{
"name": "Giraffe",
"alive": true,
"active": true,
"staticImg": "images/characters/static/static_giraffe.png",
"animatedImg": "images/characters/animated/animated_giraffe.png",
"cagedImg": "images/characters/caged/caged_giraffe.png",
"scene": [
{
"used": false,
"backgroundImg": "images/BG_images/zooBG.png",
"question": "........." ,
"answerTrue": ".......",
"answerFalse": ".......",
"choiceOne": "RUN FOR IT!",
"choiceTwo": "Stay loyal",
"choiceOneImg": "../images/snatch_images/escape_snatch.png",
"choiceTwoImg": "images/snatch_images/stay_snatch.png",
"incorrectResult": 0,
"correctAnswer": "choiceOne",
"correct": true
},
Here is the Parent, GameLogic.js that passes the currentCharacter, sceneLocation from the state that is constantly changing:
import React, { Component } from "react";
import Snatch from "./Bandersnatch";
import characters from "../characters.json";
class GameLogic extends Component {
state ={
unlockedCharacters : 0,
currentCharacter : 0,
sceneLocation : 0,
points : 0,
showCaracterSelect: true,
showMessage: false,
showSnatch: false,
showCanvas: false,
}
componentDidMount() {
}
render() {
return (
<Snatch
sceneLocation = {this.state.sceneLocation}
currentCharacter = {this.state.currentCharacter}
choiceOneAlt = "ChoiceOne"
choiceOneImg = {characters[this.state.currentCharacter].scene[this.state.sceneLocation].choiceOneImg}
choiceTwoAlt = "ChoiceTwo"
choiceTwoImg = {characters[this.state.currentCharacter].scene[this.state.sceneLocation].choiceTwoImg}
/>
)
}
}
export default GameLogic;
Then this is passed to the child component, Bandersnatch.js:
import React, { Component } from "react";
import characters from "../characters.json";
import { HeaderH2, ImageContainer, ImageSquare, ImageText, ProgressBar, Timer } from "./NewComponents/AllComponents";
const Snatch = (props) => {
return (
<>
<title>Decision Time</title>
<div className="w3-container">
<div className="container">
<HeaderH2 text="What Would You Like To Do?" />
<div className="row">
<ImageContainer
sceneLocation = {props.sceneLocation}
currentCharacter = {props.currentCharacter}
choiceOneAlt = {props.choiceOneAlt}
choiceOneImg = {props.choiceOneImg}
choiceTwoAlt = {props.choiceTwoAlt}
choiceTwoImg = {props.choiceTwoImg}
/>
{/* <ProgressBar /> */}
{/* <ImageText
sceneLocation = {props.sceneLocation}
currentCharacter = {props.currentCharacter}
/> */}
</div>
</div>
</div>
</>
);
}
export default Snatch;
Which is then passed to ImageContainer:
import React, { Component } from "react";
import ImageSquare from "./ImageSquare";
import characterObject from "../../characters.json";
const ImageContainer = (props) => {
return (
<>
<div className="col-md-6 optionOneclassName">
<ImageSquare
imgsrc={props.choiceOneImg}
altText={props.choiceOneAlt}
/>
</div>
<div className="col-md-6 optionTwoclassName">
<ImageSquare
imgsrc={props.choiceTwoImg}
altText={props.choiceTwoAlt}
/>
</div>
</>
)
};
export default ImageContainer;
And then finally accepted in the ImageSquare.js:
import React from "react";
const ImageSquare = (props) => { // passing in the img src
return (
<img src={props.imgsrc} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;
Thank you so much for your help! I am not sure if it is easier, but the repo is here
Use require to load the image. Passing path to img directly won’t work. Either you need import or require to load the image
You need to add ../ before path
Change
import React from "react";
const ImageSquare = (props) => { // passing in the img src
return (
<img src={props.imgsrc} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;
To
import React from "react";
const ImageSquare = (props) => { // passing in the img src
const path = "../"+props.imgsrc;
return (
<img src={require(path)} alt={props.altText} height="600" width="600" />
)
};
export default ImageSquare;

Modelling Dynamic Forms as React Component

How to model dynamic forms as a React Component?
For example I want to create a form shown in an image below:
How can I model this as a React component?
How can I add dynamicity to that component? For example, clicking on "+ Add" button creates another empty textbox and puts it right below the other already rendered textboxes (as shown in an image below).
Can someone help me with the code for the Form below?
In tags I see redux so I can suggest redux-form. Here you have an example of dynamic forms with redux-form.
The difference is in the fact, that beyond the state of form values, we also need to handle the state of form shape/structure.
If you render the inputs by traversing some state object, that is representing the shape of the form, than new input is just a new entry in this state object. You can easily add or remove input fields on the form by managing that state object. E.g. you can write something like this (pseudo react code):
// inputs state of math and algorithms
const state = { math: [obj1, obj2], algorithms: [obj1, obj2] } // obj ~= {id, value}
// render math inputs
const mathMarkup = state.math.map(obj => <input value={obj.value} onChange={...} />)
// add handler for math field
const addMath = () => setState(prevState => ({ math: [...prevState.math, newObj]}))
Here is the example of such form - codesandbox. It's not 100% as on your screen, but the idea should be understandable. Since there are some unclear requirements on your form, I implemented only first two sections, so you can grasp the idea. And, there are no styles :shrug:
Also, you can extract renderXyz methods to separate components, and improve state shape to meet your needs.
I can help you with a reduced way
import React , {Component} from 'react'
import { connect }from 'react-redux'
class main extends Component{
render(){
return(
<div>
<BaselineMath/>
<Algorithms />
</div>
)
}
}
const mapStateToProps = ({}) => {
return{}
}
export default connect (mapStateToProps,{})(main)
class BaselineMath extends Component{
constructor(props){
super(props);
this.state={rows:[1]}
}
_getRows{
return this.state.rows.map((res,key)=>{
return <input placeholder="etc..."/>
})
}
onClickAdd(){
let rows = this.state.rows
rows.push(1)
this.setState({
rows
})
}
render(){
return(
<div>
<Button onClick={this.onClickAdd.bind(this)}>ADD row</Button>
{this._getRows()}
</div>
)
}
}
export default (BaselineMath)
class Algorithms extends Component{
constructor(props){
super(props);
this.state={rows:[1]}
}
_getRows{
return this.state.rows.map((res,key)=>{
return <input placeholder="etc..."/>
})
}
onClickAdd(){
let rows = this.state.rows
rows.push(1)
this.setState({
rows
})
}
render(){
return(
<div>
<Button onClick={this.onClickAdd.bind(this)}>ADD row</Button>
{this._getRows()}
</div>
)
}
}
export default (Algorithms)
you can do the algorithm with anything you want

Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes

i am currently making a simple react application.
this is my index.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(
<App />,
document.getElementById('root') as HTMLElement
);
registerServiceWorker();
and here I have my app.tsx
import * as React from 'react';
import SearchBar from '../containers/price_search_bar';
interface Props {
term: string;
}
class App extends React.Component<Props> {
// tslint:disable-next-line:typedef
constructor(props) {
super(props);
this.state = {term: '' };
}
render() {
return (
<div className="App">
<div className="App-header">
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
this is my application.
</p>
<div>
<form>
<SearchBar term={this.props.term} />
</form>
</div>
</div>
);
}
}
export default App;
and also my search bar container:
import * as React from 'react';
interface Props {
term: string;
}
// tslint:disable-next-line:no-any
class SearchBar extends React.Component<Props> {
// tslint:disable-next-line:typedef
constructor(props) {
super(props);
this.state = { term: '' };
}
public render() {
return(
<form>
<input
placeholder="search for base budget"
className="form-control"
value={this.props.term}
/>
<span className="input-group-btn" >
<button type="submit" className="btn btn-secondary" >
Submit
</button>
</span>
</form>
);
}
}
export default SearchBar;
and finally I have my tsconfig.json:
{
"compilerOptions": {
"outDir": "build/dist",
"module": "esnext",
"target": "es5",
"lib": ["es6", "dom"],
"sourceMap": true,
"allowJs": true,
"jsx": "react",
"moduleResolution": "node",
"rootDir": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": false,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"node_modules/#types"
],
"noUnusedLocals": true
},
"exclude": [
"node_modules",
"build",
"scripts",
"acceptance-tests",
"webpack",
"jest",
"src/setupTests.ts"
]
}
I keep getting different errors after errors and when ever I fix one error another one appears, I am not sure what I have done that make it behave like this.
This is the latest error:
./src/index.tsx
(7,3): error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<App> & Readonly<{ children?: ReactNode; }> & Reado...'.
Type '{}' is not assignable to type 'Readonly<Props>'.
Property 'term' is missing in type '{}'.
I tried to fix it by modifying my tsconfig.json but the same error still appears, what am I doing wrong and why typescript is bahing like this. I am very new to this and by this example I am trying to udnertand how react works all together.
I solved a lot of "not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes" type of errors (Microsoft closed issue) just by declaring an object that is passed entirely to the component.
With the OP's example, instead of using term={this.props.term}, use {...searchBarProps} to get it working:
render() {
const searchBarProps = { // make sure all required component's inputs/Props keys&types match
term: this.props.term
}
return (
<div className="App">
...
<div>
<form>
<SearchBar {...searchBarProps} />
</form>
</div>
</div>
);
}
All you need is to declare the component type properly to include the props type:
interface IMyProps {
myValue: boolean,
}
const MyComponent: React.FC<IMyProps> = (props: IMyProps) => {
...
}
export default MyComponent;
Then you can use it as:
import MyComponent from '../MyComponent';
...
return <MyComponent myValue={true} />
And voila, typescript is happy. The good thing about it is that typescript is now checking for passing only the parameters they actually exist in the props interface (can prevent typos and so on).
For the standard component it would be something similar to (what's already in Swapnill's example):
class MyComponent extends React.Component<IMyProps, IMyState>{
constructor(props: IMyProps){}
}
export default MyComponent;
The problem here is not with your tslint settings. Look at the following code snippet:
interface SearchBarProps {
term: string;
optionalArgument?: string;
}
interface SearchBarState{
something: number;
}
class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
constructor(props: SearchBarProps){
super(props);
this.state = {
something: 23
};
}
render() {
const {something} = this.state;
return (
<div>{something}</div>
)
}
}
In class SearchBar extends React.Component<SearchBarProps, SearchBarState> {, SearchBarProps and SearchBarState denote type of expected props and type of state for component SearchBar respectively. You must give propTypes and stateType when you use typescript.
You can avoid giving types by using keyword any but I highly suggest you not to follow this "evil" path if you truly want to take advantage of using typescript. In your case it seems that you haven't specified type of state and used it, fixing that will solve this problem.
Edit 1
In interface SearchBarProps, optionalArgument becomes an optional argument as we add a question mark ? in front of it, so <SearchBar term='some term' /> won't show any error even if you don't pass optionalArgument explicitly.
Hope this solves your problem!
Just had this same problem.
You have member called term defined on the Prop inteface for your App class but you're not providing a value when you create your App element.
Try the following:
ReactDOM.render(<App term="Foo" />, document.getElementById('root') as HTMLElement);
I'm not really proud of that but, considering other solutions in this thread, it seems fair enough.
This example shows a custom version of #react-native-community/slider with some default properties but able to receive (and overwrite) from outside:
function CustomSlider(props: SliderProps) {
return (
<Slider
style={{ width: '100%', height: 40, marginTop: 12 }}
minimumValue={0}
minimumTrackTintColor="#000000"
maximumTrackTintColor="#000000"
{...(props as any)}
/>
);
}
I also faced the same problem. Add below code to work with .tsx components.
export interface Props {
term: string;
}
or
export type Props = {
term ?: string;
}
I dont know the exact reason, but i think typescript flag the type error during compilation phase. Let me know if it works for you.
I know my answer is somewhat off-topic, but in an Vue3 application I can reproduce the error by assigning the component the props attribute even if no props are passed:
export default defineComponent({ props:{} .... })
Just by removing the props attribute the compiler do not complains anymore.
The issue is that you are not exporting the interface, you should always export the interface props. So:
export interface Props {
term: string;
}
Is the solution.
For functional components, this syntax solves this error without React.FC boilerplate:
interface FooProps {
foo: FooType
}
function FooComponent({ foo }: FooProps): ReactElement {
...
}
I keep arriving back here because Volar is raising a similar error in my Vue 3 component. The solution is always that I am returning and empty object for props because my component template has it for convenience but I haven't used it.
When the component is used:
<template>
<div>
<MyComponent/> <!-- Squiggly error here -->
</div>
</template>
The component:
export default defineComponent({
name: 'MyComponent',
components: {},
props: {}, // Remove this
setup() {
return {};
},
});
If you're blind like me, you may bump into the following.
Instead of:
interface TimelineProps {
width: number;
}
const Timeline: FC = (props: TimelineProps) => {
...
Do:
const Timeline: FC<TimelineProps> = (props: TimelineProps) => {
^^^
What worked for me is simply changing the child Component type from React.FC to JSX.Element
Before (warning)
const Component: React.FC = () => {
After (no warning)
const Component = (): JSX.Element => {
Insted of <YourComponent product={product} /> use <YourComponent {...product} />
For components, it may be because you wrote your component like this:
<ClearButton
text={"Clear board"}
isAnimationInProgress={isAnimationInProgress}
callSetOptionMethod={clearBoard}
>
// empty space here
</ClearButton>
Instead of this:
<ClearButton
text={"Clear board"}
isAnimationInProgress={isAnimationInProgress}
callSetOptionMethod={clearBoard}
></ClearButton>
Just spread the passing props like {...searchval} .
and in the component use the props and assign the type original type of searchval.This should work

Add a class to the HTML <body> tag with React?

I'm making a modal in my React project that requires a class to be added to the body when the modal is open and removed when it is closed.
I could do this the old jQuery way by running some vanilla JavaScript which adds / removes a class, however this doesn't feel like the normal React philosophy.
Should I instead setState on my top level component to say whether the modal is open or closed? Even if I did this, as it's rendered into the div on the page it's still a side-effect to edit the body element, so is there any benefit for this extra wiring?
TL;DR use document.body.classList.add and document.body.classList.remove
I would have two functions that toggle a piece of state to show/hide the modal within your outer component.
Inside these functions I would use the document.body.classList.add and document.body.classList.remove methods to manipulate the body class dependant on the modal's state like below:
openModal = (event) => {
document.body.classList.add('modal-open');
this.setState({ showModal: true });
}
hideModal = (event) => {
document.body.classList.remove('modal-open');
this.setState({ showModal: false });
}
With the new React (16.8) this can be solved with hooks:
import {useEffect} from 'react';
const addBodyClass = className => document.body.classList.add(className);
const removeBodyClass = className => document.body.classList.remove(className);
export default function useBodyClass(className) {
useEffect(
() => {
// Set up
className instanceof Array ? className.map(addBodyClass) : addBodyClass(className);
// Clean up
return () => {
className instanceof Array
? className.map(removeBodyClass)
: removeBodyClass(className);
};
},
[className]
);
}
then, in the component
export const Sidebar = ({position = 'left', children}) => {
useBodyClass(`page--sidebar-${position}`);
return (
<aside className="...">
{children}
</aside>
);
};
Actually you don't need 2 functions for opening and closing, you could use document.body.classList.toggle
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
document.body.classList.toggle('modal-open', isOpen);
},[isOpen])
<button onCLick={()=> setIsOpen(!isOpen)}>Toggle Modal</button>
Like what #brian mentioned, try having a top-level container component that wraps around your other components. (assuming you're not using redux in your app)
In this top-level component:
Add a boolean state (eg. modalOpen) to toggle the CSS class
Add methods (eg. handleOpenModal & handleCloseModal) to modify the boolean state.
Pass the methods created above as props into your <Modal /> component
ReactJS has an official React Modal component, I would just use that: https://github.com/reactjs/react-modal