I am a beginner in developing with React. I learned with the Facebook documentation. I practice with the "Thinking in React" example (go to it). But I tried to change the solution by using nothing but functions.
Here is the result :
function ProductCategoryRow({category, key}) {
return <tr><th colSpan="2">{category}</th></tr> ;
}
function ProductRow({product, key}) {
var name = product.stocked ? product.name :
<span style={{color: 'red'}}>
{product.name}
</span>;
return (
<tr>
<td>{name}</td>
<td>{product.price}</td>
</tr>
);
}
function ProductTable({products, filterText, inStockOnly}) {
var rows = [];
var lastCategory = null;
products.forEach((product) => {
if (product.name.indexOf(filterText) === -1 || (!product.stocked && inStockOnly)) {
return;
}
if (product.category !== lastCategory) {
rows.push(<ProductCategoryRow category={product.category} key={product.category} />);
}
rows.push(<ProductRow product={product} key={product.name} />);
lastCategory = product.category;
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function handleFilterTextInput(event) { filterText = event.target.value; refresh() }
function handleInStockInput(e) { inStockOnly = e.target.checked; refresh()}
function SearchBar({filterText, inStockOnly, onFilterTextInput, onInStockInput}) {
return (
<form>
<input
type="text"
placeholder="Search..."
value={filterText}
onChange={onFilterTextInput}
/>
<p>
<input
type="checkbox"
checked={inStockOnly}
onChange={onInStockInput}
/>
{' '}
Only show products in stock
</p>
</form>
);
}
var filterText = "";
var inStockOnly = false;
function FilterableProductTable({products}) {
return (
<div>
<SearchBar
filterText={filterText}
inStockOnly={inStockOnly}
onFilterTextInput={handleFilterTextInput}
onInStockInput={handleInStockInput}
/>
<ProductTable
products={PRODUCTS}
filterText={filterText}
inStockOnly={inStockOnly}
/>
</div>
);
}
var PRODUCTS = [
{category: 'Sporting Goods', price: '$49.99', stocked: true, name: 'Football'},
{category: 'Sporting Goods', price: '$9.99', stocked: true, name: 'Baseball'},
{category: 'Sporting Goods', price: '$29.99', stocked: false, name: 'Basketball'},
{category: 'Electronics', price: '$99.99', stocked: true, name: 'iPod Touch'},
{category: 'Electronics', price: '$399.99', stocked: false, name: 'iPhone 5'},
{category: 'Electronics', price: '$199.99', stocked: true, name: 'Nexus 7'}
];
function refresh() {
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
document.getElementById('container')
);
}
refresh();
It works well but :
could I go on this way ?
is there any method to refresh de document in a better way that re-render from the root of the tree ?
Any other comment would be appreciated
You are using functional components in your implementation. These are fine to use for 'stateless' components. However, if a component has properties that will change, for example, the text in your search bar, then you will want to use React state to handle these changes. Changes on state and props of a component are the two main ways that React intelligently manages re-rendering/ refreshing.
There is no need to re-implement a refresh() function, since the power of React comes from its ability to automatically handle re-rendering based on changes to the state of our components.
Hopefully this information helps. I would recommend watching the Intro to React video and to get a grasp of what React does and why developers use it
Thanks for the informations.
I have played the video "Intro to React". It is useful for me.
But when I see the Facebook documentation that says :
ReactDOM.render() controls the contents of the container node you pass
in. Any existing DOM elements inside are replaced when first called.
**Later calls use React’s DOM diffing algorithm for efficient
updates**.
I wonder why I could not go on with my "refresh" method.
Indeed I am reluctant to dive into Class (that are not real classes !) component, state (that seam's very hard to define correctly) and all that constructor, super, props, this, lifecycle ....... carrying all the this.props. and this.state. notations ....
Certainly I will encounter some situations were it's mandatory but I would be very please to delay this as much as possible !!!
Related
hopefully someone there who can guide me on rite way. trying to create dynamic html with dynamic control. here is my example.
const [oHtml, setoHtml] = React.useState([
{
type: "html",
control: "<table><tr><td>",
},
{
type: "control",
control: (
<CustomInput
labelText="Password"
id="password"
formControlProps={{
fullWidth: true,
}}
inputProps={{
type: "password",
autoComplete: "off",
}}
/>
),
},
{
type: "html",
control: "</td></tr></table>",
},
]);
and here how i am rendering.
return (
<div>
{Object.keys(oHtml).map(function (keyName, keyIndex) {
if (oHtml[keyName].type === "html") {
console.log(oHtml[keyName].control);
return ReactHtmlParser(oHtml[keyName].control);
} else {
return oHtml[keyName].control;
}
})}
</div>
);
issue is when React run <table><tr><td> its auto fixing tag and making it <table><tr><td></td></tr></table>
what i want to control inside td.
is there any way i can disable react to add missing tags itself?
The thing is that ReactHtmlParser can't be used to parse partial html.
You can try
return ReactHtmlParser(
oHtml.map(v => v.control).join("")
)
Btw, your oHtml is an array, so no need for Object.keys.
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 :)
I have a json that consists of a parent name and kids that the parent has.
I have a code that grabs the name of the parent and the kids of the json . Based on the amount of kids, input boxes are created so you can enter the name of each kid. But I am stuck. When I click the button how would I print a message that starts with the parent name and combines the values from each input box from the values of the json and the boxes that allow me to name the child
for instance when I click 'jim' from the dropdown, 2 input boxes shows up that says 'child1' and 'child2' and two boxes shows up next to them because he has two children. if i name child1 "james" and child2 "mary" and push the button how can I print a message in the final input box that says
"Jim -- child1=james -- child2=mary"
import React from 'react';
class AppEX extends React.Component {
constructor() {
super();
this.state = {
kids: null,
parentname: null,
parent: [
{ name: 'will', kids: ['child1', 'child2'] },
{ name: 'kia', kids: ['child1'] },
{ name: 'jim', kids: ['child1', 'child2'] }
]
};
}
handleParentChoice = e => {
e.persist();
this.setState({
parentname: e.target.value
});
};
render() {
const namelist = [];
this.state.parent.forEach(e => {
namelist.push({ value: e.name, label: e.name });
});
return (
<div>
<select name="select" onChange={this.handleParentChoice}>
{namelist.map(n => (
<option key={n.value} value={n.value}>
{n.label}
</option>
))}
</select>
<br />
{this.state.parentname &&
this.state.parent
.find(p => p.name === this.state.parentname)
.kids.map(k => (
<div>
<input
key={k}
type="text"
value={k}
disabled={true}
/>
<input key={k} type="text" />
</div>
))}
<br />
<button type="button" value="render" onClick={this.print} />
<input type="text" disabled={true} />
</div>
);
}
}
export default AppEX;
I created an example here, which does what you need.
Note that I had to add/modify some key attributes at places, because there were some console errors.
Also you need to synchronize input values to state at some point. In this example I do it on each keystroke, but you must be aware of the performance implications of this. You may choose different timing for the sync (on blur, with some debounce, etc.), it's up to you.
I modified inputs' name attributes so that each one has unique one, and can be recognized in the function which syncs to state (onChildNameChange).
And btw there are some nice libraries which can spare you the boilerplate of syncing, like react-hook-form (functional components, only) and formik.
As for printing the output string, I used Array.reduce to build it.
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 = {
};
I have a little issue with a HTML select with AngularJS. When I do a petition to my API I get one of the values as an integer, but when I try to autofill a select with it I can't set de "value" correctly.
In this picture you can se what the HTML is receiving and the values that I want to set
Are there any way to cast this value?
Thanks in advance :)
EDITED:
The controller to get customer data and fill the form
.controller('CustomerDetailCtrl', ['Customer', '$scope', '$sessionStorage', '$stateParams', '$ionicPopup', function (Customer, $scope, $sessionStorage, $stateParams, $ionicPopup) {
if ($sessionStorage.auth) {
Customer.get({data: $stateParams.customerId + '_' + $sessionStorage.user_id}).$promise.then(function (data) {
if (data.response && $sessionStorage.role === 1) {
$scope.customer = data.response[0];
if (data.history) {
$scope.histories = data.history;
}
} else {
console.log('Error de accesso...');
}
})
}
$scope.addTask = function (customer) {
alert('add task!');
}
$scope.deleteTask = function (customer, history) {
alert('delete task!');
}
}])
The form:
<label class="item item-input item-select">
<div class="input-label">
Cliente avisado?
</div>
<select name="informed" ng-model="customer.informed" required>
<option value="0">NO</option>
<option value="1">SI</option>
</select>
</label>
And here a picture of the data from de API:
I know that you've already received an answer on this, but I wanted to show you one other potential option that doesn't involve having to change your data from an int to string. If you define the options for your select in your controller (or in a service if this will be used in multiple different places throughout your app) then you can take advantage of ng-options and its ability to use a value other than a string.
Here's an example (obviously I've hardcoded some things and put this all in a single module - not something you'd do in a real app).
JS:
angular.module('app', [])
.controller('ctrl', function($scope){
// select options (if these are common maybe store them in a service
// so you can share them in many controllers without duplicating the code)
$scope.selectOptions = [
{
text: 'NO',
value: 0
},
{
text: 'SI',
value: 1
}];
// sample data
$scope.customer = {
address: 'San Rosendo 11',
date: '2016-03-16T16:19:13+0100',
email: 'Montes',
equipment: 'PC',
id: 262,
informed: 1,
lastName: 'Montes',
location: 'Tienda',
name: 'Juanma',
notes: '',
pass: 'no tiene',
phone: '900112233',
price: '0',
status: 'Pendiente',
tasks: 'dfsdf'
};
});
HTML:
<div ng-app='app' ng-controller='ctrl'>
<select ng-model='customer.informed' ng-options='option.value as option.text for option in selectOptions'></select>
</div>
jsFiddle
Define a default value somewhere in your controller: $scope.customer.informed = "NO";