Custom renderProgressBar cannot be displayed - react-pdf-viewer

The react-pdf-viewer can be provided with a custom print loading dialog. The Code below does not overwrite the default loading dialog. What is wrong?
export function myPDF({ ...props }): JSX.Element {
const renderProgressBar = React.useCallback(
(numLoadedPages: number, numPages: number, onCancel: () => void) => (
<div>
loading
</div>
),
[],
)
const printPluginInstance = printPlugin({
renderProgressBar,
})
const { Print } = printPluginInstance
return (
<Worker workerUrl="https://unpkg.com/pdfjs-dist#2.11.338/build/pdf.worker.min.js">
<Print>
{(props: RenderPrintProps) => (
<button
onClick={props.onClick}
>
Print
</button>
)}
</Print>
</Worker>
)
}

Related

Not closing camera in zxing

In the Zxing library, I want to close the camera when the user clicks cancel. So I used a button and add onclick event to it. it is calling resetReader method. I called this method after gets a barcode value or in the cancel button onclick event.If it is getting barcode values, this resetReader method works perfectly. if we cancel, the camera doesn't stop. Am I missing something?
const codeReader = new BrowserMultiFormatReader(hints);
const resetReader = () => {
codeReader.reset();
codeReader.stopContinuousDecode();
};
for those who haven't figured it out yet? I have found a solution to this problem. Harendrra's solution didn't work for me, but this one did in combination with usestate. For my project the code uses Bootstrap. So when I click on a button the Modal appears. The camera loads. When I click on the Close button the camera disappears. Hope this is a solutions for everyone, enjoy ;-)
export default function Example(props) {
// codeReader
const [codeReader, setReader] = useState(new BrowserMultiFormatReader());
const [videoInputDevices, setVideoInputDevices] = useState([]);
const [selectedVideoDevice, selectVideoDevice] = useState('');
useEffect(() => {
(async () => {
const videoInputDeviceList = await codeReader.listVideoInputDevices();
setVideoInputDevices(videoInputDeviceList);
if (videoInputDeviceList.length > 0 && selectedVideoDevice == null) {
selectVideoDevice(videoInputDeviceList[0].deviceId);
}
})();
}, [codeReader, selectedVideoDevice]);
const handleShow = () => {
setBrand('');
// Open modal.
setShow(true);
codeReader.decodeFromVideoDevice(selectedVideoDevice, 'videoElement', (res) => {
setCanClose(true);
if (res) {
const rawText = res.getText();
axios
.get(`https://world.openfoodfacts.org/api/v0/product/${rawText}.json`)
.then((result) => {
// set data
setBrand(result.data.product.brands);
// close modal
setShow(false);
// codeReader reset
codeReader.reset();
})
.catch((err) => console.log('error', err));
}
});
};
const handleClose = () => {
// codeReader reset.
setReader(codeReader.reset());
// Close modal
setShow(false);
// Set new codeReader.
// The solution for the error messages after the codeReader reset.
// This will build the codeReader for the next time.
setReader(new BrowserMultiFormatReader(hints));
};
return (
<Fragment>
<div className='py-2'>
<div>Brand: {brand}</div>
<Button variant='primary' onClick={handleShow}>
Launch static backdrop modal
</Button>
<Modal show={show} onHide={handleClose} backdrop='static' keyboard={false} centered id='scanProductModal'>
<Modal.Body>
<div
onChange={(event) => {
const deviceId = event.target.value;
selectVideoDevice(deviceId);
}}
>
<div className='button-group-top'>
<select className='form-select form-select-sm' aria-label='Default select example'>
{videoInputDevices &&
videoInputDevices.map((inputDevice, index) => {
return (
<option value={inputDevice.deviceId} key={index}>
{inputDevice.label || inputDevice.deviceId}
</option>
);
})}
</select>
</div>
<video id='videoElement' width='600' height='400' />
<Button className='btn btn-danger' onClick={handleClose}>
Close
</Button>
</div>
</Modal.Body>
</Modal>
</Fragment>
);
}
Yes, I resolved. You have to create codeReader object at the top of the Class. Try this code.
import "../App.css";
import { BrowserBarcodeReader } from "#zxing/library";
class Barcode extends React.Component {
codeReader = new BrowserBarcodeReader();
constructor(props) {
super(props);
this.state = { reader: {}, selectedDevice: "" };
this.startButton = this.startButton.bind(this);
this.resetButton = this.resetButton.bind(this);
this.getBarcode = this.getBarcode.bind(this);
}
componentDidMount() {
this.getBarcode();
}
startButton() {
console.log("start", this.codeReader);
this.codeReader
.decodeOnceFromVideoDevice(this.state.selectedDevice, "video")
.then(result => {
document.getElementById("result").textContent = result.text;
})
.catch(err => {
console.error(err.toString());
document.getElementById("result").textContent = err;
});
console.log(
`Started continous decode from camera with id ${this.state.selectedDevice}`
);
}
resetButton() {
this.codeReader && this.codeReader.reset();
document.getElementById("result").textContent = "";
}
getBarcode() {
let selectedDeviceId;
return this.codeReader.getVideoInputDevices().then(videoInputDevices => {
const sourceSelect = document.getElementById("sourceSelect");
selectedDeviceId = videoInputDevices[0].deviceId;
if (videoInputDevices.length > 1) {
videoInputDevices.forEach(element => {
const sourceOption = document.createElement("option");
sourceOption.text = element.label;
sourceOption.value = element.deviceId;
sourceSelect.appendChild(sourceOption);
});
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
const sourceSelectPanel = document.getElementById(
"sourceSelectPanel"
);
sourceSelectPanel.style.display = "block";
}
this.setState({
selectedDevice: selectedDeviceId
});
})
.catch(err => {
alert(err);
});
}
render() {
return (
<div>
<h2>Barcode</h2>
{Object.keys(this.codeReader).length > 0 && (
<div>
<div>
<button
className="button"
id="startButton"
onClick={this.startButton}
>
Start
</button>
<button
className="button"
id="resetButton"
onClick={this.resetButton}
>
Reset
</button>
</div>
<div>
<video
id="video"
width="600"
height="400"
style={{ border: "1px solid gray" }}
></video>
</div>
<label>Result:</label>
<pre>
<code id="result"></code>
</pre>
</div>
)}
</div>
);
}
}
export default Barcode; ```

react hooks setState does not properly re-render component

I have a component(toggle Menu List) that is supposed to load a child component(toggle Menu Items) on button click.
Here is How it works.
'btnId' init state = null
-> button click
-> update state to index number 1
-> (btnId !== null) && load the child component
However the child component is not being displayed on state update.
If I set init state to 1, it is displayed on button click.
toggleMenuList.js
import React, { useState, useRef } from 'react';
import Button from '#material-ui/core/Button';
import { withStyles } from '#material-ui/core/styles';
/* --- Components --- */
import Loader from '../../shared/loader';
const ToggleMenuItems = Loader({
loader: () =>
import('./toggleMenuItems' /* webpackChunkName: 'ToggleMenuItems' */),
});
const styles = theme => ({
...
});
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [open, setOpen] = useState(false);
const [btnId, setBtnId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = async id => {
await setBtnId(id);
return setOpen(prevOpen => !prevOpen);
};
const handleClose = event => {
...
};
console.log('Toggle Menu List is rendered');
console.log('btnId: ', btnId);
return (
<React.Fragment>
<div className={`nav-menu ${classes.root}`}>
{navAdminList.map(e => (
<Button
key={e.id}
ref={anchorRef}
aria-owns={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
className={e.className}
>
{e.name}
</Button>
))}
</div>
{btnId !== null && (
<ToggleMenuItems
handleClose={handleClose}
open={open}
anchorRef={anchorRef}
items={navAdminItems[btnId]}
/>
)}
</React.Fragment>
);
};
export default withStyles(styles)(ToggleMenuList);
toggleMenuItems.js
const ToggleMenuItems = ({ handleClose, open, anchorRef, items }) => {
console.log('Toggle Menu Items is rendered.');
console.log('open: ', open);
return (
<Popper
open={open}
anchorEl={anchorRef.current}
keepMounted
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper id="menu-list-grow">
<ClickAwayListener onClickAway={handleClose}>
<MenuList>
{items.map(e => (
<MenuItem key={e.id} onClick={handleClose}>
<Link to={e.to} className={e.className}>
{e.name}
</Link>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
};
export default ToggleMenuItems;
Here is console logs i gets.
* On page load
toggleMenuList.js: Toggle Menu List is rendered
toggleMenuList.js: btnId: null
* On button click
toggleMenuList.js: Toggle Menu List is rendered
toggleMenuList.js: btnId: 1
toggleMenuList.js: Toggle Menu List is rendered
toggleMenuList.js: btnId: 1
toggleMenuItems.js: Toggle Menu Items is rendered.
toggleMenuItems.js: open: true
result
The state value is updated.
The child component seems to be loaded. (console.log gets fired)
But It is not displayed.
I resolved the problem with a different approach. I set activeId state with initial value 'null' which updates with 'clicked button id' on button click. And load the child component only when 'button Id' matches 'activeId'.
And I also add this condition in child component.
const isOpen = activeId === id;
return (
<Popper
open={isOpen}
This way, when one button's open is true, the rest is set to false.
toggleMenuList.js
const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
const [activeId, setActiveId] = useState(null);
const anchorRef = useRef(null);
const handleToggle = id => {
setActiveId(id);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.include(event.target)) {
return;
}
setActiveId(null);
};
return (
<React.Fragment>
<div className={`nav-menu ${classes.root}`}>
{navAdminList.map(e => (
<div key={e.id}>
<Button
ref={anchorRef}
aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={() => handleToggle(e.id)}
className={e.className}
>
{e.name}
</Button>
{activeId === e.id && (
<ToggleMenuItems
id={e.id}
activeId={activeId}
handleClose={handleClose}
anchorRef={anchorRef}
items={navAdminItems[e.id]}
/>
)}
</div>
))}
</div>
</React.Fragment>
);
};
toggleMenuItems.js
const ToggleMenuItems = ({ id, activeId, handleClose, anchorRef, items }) => {
const isOpen = activeId === id;
return (
<Popper
open={isOpen}
anchorEl={anchorRef.current}
keepMounted
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper id="menu-list-grow">
<ClickAwayListener onClickAway={() => handleClose(id)}>
<MenuList>
{items.map(e => (
<MenuItem key={e.id} onClick={() => handleClose(id)}>
<Link to={e.to} className={e.className}>
{e.name}
</Link>
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
};

How to use setFieldValue and pass the value as props between components

I'm trying to use ant design forms in my sample registration form, when i'm trying to use setFieldsValue it throws error as "Cannot use setFieldsValue unless getFieldDecorator is used". But I had already used getFieldDecorator in my code.Here is a sample of my code.
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.setFieldsValue({
fname: fvalue
});
}
render(){
const { getFieldDecorator } = this.props.form
return (
<Row gutter={4}>
<Col className="reg-personal-details-grid-column" span={24}>
<FormItem {...formItemLayout} label="First Name">
{getFieldDecorator("firstName", {
rules: [
{
required: true
}
]
})(
<Input
placeholder="First Name"
required
name="firstName"
onChange={this.handleChange}
/>
)}
</FormItem>
</Col>
</Row>
)
}
fname is not defined in getFieldDecorator.
You should do this:
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.form.setFieldsValue({
[fname]: fvalue
});
}
You can create your own function to do this:
export const setFieldValue = (
form: FormInstance,
name: NamePath,
value: string
): void => {
if (form && form.getFieldValue(name)) {
const fixname: string[] = [];
if (typeof name == 'object') {
name.forEach((node: string) => {
fixname.push(node);
});
} else {
fixname.push(String(name));
}
let fieldsValue: unknown;
fixname.reverse().forEach((node: string) => {
fieldsValue = {
[String(node)]: fieldsValue != undefined ? fieldsValue : value,
};
});
form.setFieldsValue(fieldsValue);
}
};
and you can use like that
setFieldValue(form, 'phone', '1111111111');
or
setFieldValue(form, ['phones', 'mobile'], '2222222222');
In Antd v4, you only required to call the setFieldsValue in useEffect function.
const [form] = Form.useForm()
useEffect(() => {
//companyInfo -> dynamic obj fetched from API
form.setFieldsValue(companyInfo);
}, [companyInfo, form]);
Then in Form use the form prop as follow:
<Form
//passing form prop
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 14 }}
onFinish={onFinish}
labelAlign="left"
colon={false}
>
you can use it like this
const formRef = useRef(null);
useEffect(() => {
formRef.current?.setFieldsValue({
name: data?.firstName,
});
}, [data]);
return (
<Form ref={formRef}>
<Form.Item name="name">
<Input/>
</Form.Item>

custom button in inline toolbar that calls custom function

I want to combine the inline toolbar with the example from draft.js to insert a link into the editor.
Thanks to the draft.js plugin FAQ I am able to add a decorator to the draft-js-plugin editor which inserts a link on a button click.
Now I want to put this button inside the inline-toolbar from draft-js-plugins. That doesn't seem to work. This is what I've done so far:
Editor.jsx
...
const inlineToolbarPlugin = createInlineToolbarPlugin({
theme: {buttonStyles, toolbarStyles},
structure: [..., LinkButton]
});
const {InlineToolbar} = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin];
class RMSEditor extends Component {
...
render() {
return (
<div>
<div className={editorStyles.editor}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
decorators={this.decorator}
ref={(element) => {
this.editor = element;
}}
readOnly={this.state.readOnly}
/>
<InlineToolbar />
</div>
</div>
);
}
LinkButton.jsx
import classNames from "classnames/bind";
import React from "react";
import {Glyphicon} from "react-bootstrap";
import styles from "./buttonStyles.less";
const cx = classNames.bind(styles);
const LinkButton = () => {
return (
<div className={cx('buttonWrapper')} onClick={(e) => {
console.log("Div", e);
}}>
<button className={cx('button')} onClick={(e) => {
console.log("Button", e);
}}><Glyphicon glyph="link"/></button>
</div>
)
};
export default LinkButton;
This way, I have managed to get a Button that shows up in the inline toolbar. But when I clicked on that button, nothing happens. I expacted that one of the onClick handlers will fire but that is not the case.
Full source code
Here you can find my full sourcecode as I don't want to put only the relevant parts directly under the questions. Please notice that the code will not work in the run snipped thingy as I have no clue how to get it to work there without setting up the hole wabpack thing.
MentionsEditor.jsx
import {CompositeDecorator, EditorState, RichUtils} from "draft-js";
import {BoldButton, CodeButton, ItalicButton, UnderlineButton, UnorderedListButton} from "draft-js-buttons";
import createInlineToolbarPlugin from "draft-js-inline-toolbar-plugin";
import {defaultSuggestionsFilter} from "draft-js-mention-plugin"; // eslint-disable-line import/no-unresolved
import Editor from "draft-js-plugins-editor"; // eslint-disable-line import/no-unresolved
import React, {Component, PropTypes} from "react";
import {Button} from "react-bootstrap";
import buttonStyles from "./buttonStyles";
import editorStyles from "./editorStyles";
import LinkButton from "./InsertLinkButton";
import toolbarStyles from "./toolbarStyles";
const inlineToolbarPlugin = createInlineToolbarPlugin({
theme: {buttonStyles, toolbarStyles},
structure: [BoldButton, ItalicButton, UnderlineButton, CodeButton, UnorderedListButton, LinkButton]
});
const {InlineToolbar} = inlineToolbarPlugin;
const plugins = [inlineToolbarPlugin];
class RMSEditor extends Component {
constructor(props) {
super(props);
}
decorator = [
{
strategy: function findLinkEntities(contentBlock, callback, contentState) {
contentBlock.findEntityRanges(
(character) => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'LINK'
);
},
callback
);
},
component: function (props) {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url}>
{props.children}
</a>
);
}
}
];
state = {
editorState: EditorState.createEmpty(),
};
onChange = (editorState) => {
this.setState({
editorState,
});
};
editorLink = function (props) {
const {url} = props.contentState.getEntity(props.entityKey).getData();
return (
<a href={url} style={{color: "blue"}}>
{props.children}
</a>
);
};
focus = () => {
this.editor.focus();
};
insertLink = (e) => {
e.preventDefault();
const {editorState} = this.state;
const contentState = editorState.getCurrentContent();
const contentStateWithEntity = contentState.createEntity(
'LINK',
'MUTABLE',
{url: "https://example.com"}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const newEditorState = EditorState.set(editorState, {currentContent: contentStateWithEntity});
this.setState({
editorState: RichUtils.toggleLink(
newEditorState,
newEditorState.getSelection(),
entityKey
)
}, () => {
setTimeout(() => {
this.focus()
}, 0);
});
};
render() {
return (
<div>
<Button onClick={this.insertLink}>insert URL</Button>
<div className={editorStyles.editor}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
decorators={this.decorator}
ref={(element) => {
this.editor = element;
}}
readOnly={this.state.readOnly}
/>
<InlineToolbar />
</div>
</div>
);
}
}
RMSEditor.propTypes = {
stakeholder: PropTypes.object.isRequired
};
export default RMSEditor;
LinkButton.jsx
import classNames from "classnames/bind";
import React from "react";
import {Glyphicon} from "react-bootstrap";
import styles from "./buttonStyles.less";
const cx = classNames.bind(styles);
const LinkButton = () => {
return (
<div className={cx('buttonWrapper')} onClick={(e) => {
console.log("Div", e);
}}>
<button className={cx('button')} onClick={(e) => {
console.log("Button", e);
}}><Glyphicon glyph="link"/></button>
</div>
)
};
export default LinkButton;
This works for me:
const MyButton = (props) => {
return (
<div className={props.className} onMouseDown={(e)=>e.preventDefault()}>
<button onClick={() => props.createComment('Hello')}>
Comment
</button>
</div>
)
}
const inlineToolbarPlugin = createInlineToolbarPlugin({
structure: [props => <MyButton {...props} createComment={(text) => alert(text)} />]
});
const { InlineToolbar } = inlineToolbarPlugin;
export default class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { editorState } = this.state;
return (
<div className="editor">
<Editor
editorState={editorState}
onChange={this.onChange}
plugins={[inlineToolbarPlugin]}
ref={(element) => { this.editor = element; }}
/>
<InlineToolbar />
</div>
)
}
}
Basically, this is what you need to do:
1) import the inline toolbar plugin (bonus there is a separator in there)
import createInlineToolbarPlugin, {Separator} from 'draft-js-inline-toolbar-plugin'
2) instantciate the plugin like this:
const inlineToolbarPlugin = createInlineToolbarPlugin({
structure: [
BoldButton,
ItalicButton,
UnderlineButton,
CodeButton,
Separator,
],
})
2.1) you can get those components and more from draft-js-buttons
3) add to the array more components (let's add a video button component)
AddVideoButton
this is what it looks like
import React, { Component, PropTypes } from 'react'
import classnames from 'classnames'
import { AtomicBlockUtils, RichUtils } from 'draft-js'
import createVideoPlugin from 'draft-js-video-plugin'
const {types} = createVideoPlugin()
export default class AddVideoButton extends Component {
static propTypes = {
getEditorState: PropTypes.func,
setEditorState: PropTypes.func,
style: PropTypes.shape(),
theme: PropTypes.shape(),
}
// toggleStyle(event){
// const { setEditorState, getEditorState, style } = this.props
//
// event.preventDefault()
// setEditorState(
// RichUtils.toggleInlineStyle(
// getEditorState(),
// style
// )
// )
// }
toggleVideo(event){
const { setEditorState, getEditorState } = this.props
event.preventDefault()
const style = { width: 100}
const editorState = getEditorState()
const currentContent = editorState.getCurrentContent()
const selectionState = editorState.getSelection()
const anchorKey = selectionState.getAnchorKey()
const currentContentBlock = currentContent.getBlockForKey(anchorKey)
const start = selectionState.getStartOffset()
const end = selectionState.getEndOffset()
const src = currentContentBlock.getText().slice(start, end)
if (RichUtils.getCurrentBlockType(editorState) === types.ATOMIC) {
return editorState
}
const contentState = editorState.getCurrentContent()
const contentStateWithEntity = contentState.createEntity(
types.VIDEOTYPE,
'IMMUTABLE',
{ src, style }
)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey()
const newEditorState = AtomicBlockUtils.insertAtomicBlock(editorState, entityKey, ' ')
return setEditorState(newEditorState)
}
render(){
const { theme, style, getEditorState } = this.props
const styleIsActive = () => getEditorState().getCurrentInlineStyle().has(style)
return (
<div
className={theme.buttonWrapper}
onMouseDown={(e)=>e.preventDefault()}
>
<button
className={classnames({
[theme.active]:styleIsActive(),
[theme.button]:true,
})}
onClick={::this.toggleVideo}
>
<svg height="24" viewBox="0 0 120 100" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M111.374,20.922c-3.151-3.159-7.557-5.128-12.374-5.125H29c-4.817-0.002-9.222,1.966-12.375,5.125 c-3.159,3.152-5.128,7.557-5.125,12.375v40.038c-0.002,4.818,1.966,9.223,5.125,12.375c3.152,3.158,7.557,5.129,12.375,5.125h70 c4.817,0.004,9.224-1.967,12.375-5.125c3.159-3.152,5.128-7.557,5.125-12.375V33.296C116.503,28.479,114.534,24.074,111.374,20.922z M104.624,78.959c-1.454,1.447-3.413,2.328-5.624,2.33H29c-2.211-0.002-4.17-0.883-5.625-2.33c-1.447-1.455-2.328-3.414-2.33-5.625 V33.296c0.002-2.211,0.883-4.17,2.33-5.625c1.455-1.447,3.413-2.328,5.625-2.33h70c2.211,0.002,4.17,0.883,5.625,2.33 c1.447,1.455,2.327,3.413,2.329,5.625v40.038C106.952,75.545,106.072,77.504,104.624,78.959z" fill="#232323"/><path d="M77.519,50.744L57.45,39.161c-0.46-0.266-0.971-0.397-1.483-0.397c-0.513,0-1.023,0.131-1.484,0.397 c-0.918,0.528-1.483,1.509-1.483,2.569v23.171c0,1.061,0.565,2.04,1.483,2.57c0.46,0.267,0.971,0.396,1.484,0.396 c0.513,0,1.023-0.13,1.483-0.396l20.069-11.586c0.918-0.531,1.482-1.51,1.482-2.571C79.001,52.253,78.437,51.274,77.519,50.744z" fill="#232323"/>
<path d="M0 0h24v24H0z" fill="none" />
</svg>
</button>
</div>
)
}
}
or
you can imitate anything out of the draft-js-buttons
Let me know if I helped or if you have any other questions

checkbox does not retain state in React

when i click a checkbox in a tag panel, then it becomes checked as expect. But when i switch to another panel, and then switch back, i found that checkbox which should be checked turn to unchecked. how do i fix this?
here is my code, and i use react-tab-panel from npm.
Container:
export default class Container extends Component {
static propTypes = {
};
constructor(props) {
super(props);
this.state = {
chosenItem: {
a: [],
b: [],
c: [],
d: []
}
};
}
handleCheckBoxChange = (e) => {
const value = e.target.value;
const item = e.target.attributes.getNamedItem('data-tag').value;
const chosenItem = this.state.chosenItem;
const index = chosenItem[item].indexOf(value);
if (index > -1) {
chosenItem[item].splice(index, 1);
}
chosenItem[item].push(value);
}
render() {
return (
<div>
<Panel
handleCheckBoxChange={this.handleCheckBoxChange}
/>
</div>
);
}
}
Panel:
import TabPanel from 'react-tab-panel';
import 'react-tab-panel/index.css';
import CheckBox from './CheckBox.jsx';
export default function Panel({
handleCheckBoxChange
}) {
const tabList = {
a: ['1', '2'],
b: ['1', '2'],
c: ['1', '2'],
d: ['1', '2']
};
return (
<TabPanel
tabPosition="left"
>
{
Object.keys(tabList).map((key) => {
return (
<form
key={key}
tabTitle={key}
>
{
tabList[key].map((item) =>
<CheckBox
key={item}
value={item}
handleCheckBoxChange={handleCheckBoxChange}
tag={key}
/>
)
}
</form>
);
})
}
}
</TabPanel>
);
}
Checkbox:
export default function CheckBox({
tag,
value,
handleCheckBoxChange
}) {
return (
<div>
<label>
<input
type="checkbox"
value={value}
onChange={handleCheckBoxChange}
data-tag={tag}
/>
{value}
</label>
</div>
);
}
You should consider using Flux or Redux for storing your application state tree.