material-ui textfield with japanese - material-ui

Excuse my poor English.
'TextField' of material-ui have problem with japanese input.
when use it inside 'Dialog' tag.
First letter is determined without consideration.
for example, entering 'da' should be 'だ', 'pa' should be 'ぱ'.
but it become 'dあ' and 'pあ' because first letter is determined automatically.
when first letter is entered, it should be suspended
until second letter inputted.
does anyone have idea?
import React, { Component } from 'react';
import Dialog from 'material-ui/Dialog';
import TextField from 'material-ui/TextField';
export default class MyModal extends Component {
constructor(props) {
super(props);
this.state = {
question: '',
};
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
this.setState({
question: event.target.value,
});
}
render() {
return (
<Dialog
open
>
<TextField
value={this.state.question}
onChange={this.onInputChange}
/>
</Dialog>
);
}
}

I think it's a material-ui bug. I found 2 solutions to work around it.
1: Don't put value state of TextField in Dialog. You should write like below:
class MyForm extends Component {
constructor(props) {
super(props);
this.state = {
question: '',
};
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
this.setState({
question: event.target.value,
});
}
render() {
return (
<TextField
value={this.state.question}
onChange={this.onInputChange}
/>
);
}
}
export default class MyModal extends Component {
render() {
return (
<Dialog
open
>
<MyForm />
</Dialog>
);
}
}
2; Or you can extend material-ui TextField with a little fix. This way is pretty dangerous. But it works fine for me now. (I'm using material-ui 0.15.4)
export default class FixedTextField extends mui.TextField {
handleInputChange = (event) => {
if (this.props.onChange) this.props.onChange(event, event.target.value);
}
}

Related

Preventing users from entering non-digits in input text field with React native

I dont want to allow user to type the alphabets in the input as per the same functionality of keypressevent of jquery/javascript.
you should use a TextInput Component white the prop keyboardType = {"number-pad"}
import { TextInput } from "react-native";
then use it as
<TextInput
keyboardType = {"number-pad"}
// add more props ...
/>
for all the props you can add see this link
Try replace method in JS by replacing non alphabet character with empty string.
Code
import React, { Component } from 'react';
import { AppRegistry, TextInput } from 'react-native';
export default class UselessTextInput extends Component {
constructor(props) {
super(props);
this.state = { inputText: '' };
}
onChanged (text) {
this.setState({
inputText: text.replace(/[A-Za-z]/g, ''),
});
}
render() {
return (
<TextInput
style={{height: 40, borderColor: 'gray', borderWidth: 1, marginTop: 200}}
onChangeText={(text)=> this.onChanged(text)}
value={this.state.inputText}
/>
);
}
}
// skip this line if using Create React Native App
AppRegistry.registerComponent('AwesomeProject', () => UselessTextInput);
You can check by using the isNaN() JS built-in function in your input to prevent the user to type alphabet letters;
const Text = "hello";
const Number = "1234";
console.log(isNaN(Text)) // true
console.log(isNaN(Number)) // false

React DnD - "Cannot have two HTML5 backends at the same time."

I am trying to make a POC with Rails5, action Cable, React and Rails and React DnD.
The purpose is to make an app like trello but for an recruitment process.
My front is in ReactJS.
I have 3 components, first, the container call "Candidates", this component call 2 "CardBoard" components that call "Card" component.
I user react DnD library for draggable card and droppable CardBoard. when i drop card on cardboard, i use a post call and a websocket(action cable from rails5) for update my state. I don't understand why i have this message after the post call :
Uncaught Error: Cannot have two HTML5 backends at the same time.
at HTML5Backend.setup (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4175), <anonymous>:87:15)
at DragDropManager.handleRefCountChange (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:3566), <anonymous>:52:22)
at Object.dispatch (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4931), <anonymous>:186:19)
at HandlerRegistry.addSource (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:3594), <anonymous>:104:18)
at registerSource (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4294), <anonymous>:9:27)
at DragDropContainer.receiveType (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:146:32)
at DragDropContainer.receiveProps (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:135:14)
at new DragDropContainer (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:1793), <anonymous>:102:13)
at eval (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4399), <anonymous>:295:18)
at measureLifeCyclePerf (eval at <anonymous> (webpack-bundle.self-7b1a342….js?body=1:4399), <anonymous>:75:12)
Candidate.jsx =
import React, { PropTypes } from 'react';
import { DragDropContextProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import CardBoard from './CardBoard.jsx';
export default class Candidates extends React.Component {
constructor(props, _railsContext) {
super(props);
this.state = {
candidates: this.props.candidates
}
this.filterByStatus = this.filterByStatus.bind(this)
}
componentDidMount() {
this.setupSubscription();
}
setupSubscription() {
App.candidate = App.cable.subscriptions.create("CandidateChannel", {
connected: () => {
console.log("User connected !")
},
received: (data) => {
this.setState({ candidates: data.candidates })
},
});
}
render() {
return (
<DragDropContextProvider backend={HTML5Backend}>
<div className="recruitment">
{
["À Rencontrer", "Entretien"].map((status, index) => {
return (
<CardBoard candidates={(this.filterByStatus({status}))} status={status} key={index} />
);
})
}
</div>
</DragDropContextProvider>
);
}
}
CardBoard.jsx =
import React, { PropTypes } from 'react';
import Card from './Card.jsx';
import { DropTarget } from 'react-dnd';
import ItemTypes from './ItemTypes';
const cardTarget = {
drop(props: Props) {
var status = ''
if(props.status == "À Rencontrer") {
status = 'to_book'
} else {
status = 'interview'
}
return { status: status };
},
};
#DropTarget(ItemTypes.CARD, cardTarget, (connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
canDrop: monitor.canDrop(),
}))
export default class CardBoard extends React.Component<Props> {
constructor(props, _railsContext) {
super(props);
}
render() {
const { canDrop, isOver, connectDropTarget } = this.props;
const isActive = canDrop && isOver;
return connectDropTarget(
<div className={`${this.props.status} ui cards`}>
<h2>{`${this.props.status} (${this.props.candidates.length})`}</h2>
{
(this.props.candidates).map((candidate, index) => {
return <Card candidate={candidate} key={index} />
})
}
{ isActive?
'Release to drop' : 'drag a card here'
}
</div>
);
}
}
Card.jsx=
import React, { PropTypes } from 'react';
import { DragSource } from 'react-dnd';
import ItemTypes from './ItemTypes';
const cardSource = {
beginDrag(props) {
return {
candidate_id: props.candidate.id,
};
},
endDrag(props, monitor) {
const item = monitor.getItem();
const dropResult = monitor.getDropResult();
if (dropResult) {
console.log(`You dropped ${item.candidate_id} vers ${dropResult.status} !`);
$.post(`/update_status/${item.candidate_id}/${dropResult.status}`);
}
},
};
#DragSource(ItemTypes.CARD, cardSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}))
export default class Card extends React.Component {
constructor(props, _railsContext) {
super(props);
}
render() {
const { isDragging, connectDragSource } = this.props;
const { name } = this.props;
const opacity = isDragging ? 0 : 1;
var candidate = this.props.candidate;
return (
connectDragSource(
<div className="card" key={candidate.id} style={{opacity}}>
<div className="content">
<div className="header">{`${candidate.first_name} ${candidate.last_name}`}</div>
<div className="description">
{candidate.job_title}
</div>
<span className="right floated">
<i className="heart outline like icon"></i>
{candidate.average_rate}
</span>
</div>
</div>
)
);
}
}
for better comprehension, here a gif of my feature and his bug :
feature with bug
here my github repo with all code
Just try to make the context of HTML5Backend a singleton. You can check the codes at:
https://github.com/react-dnd/react-dnd/issues/186
https://github.com/react-dnd/react-dnd/issues/708
I faced with similar issue and found workaround by moving
<DragDropContextProvider></DragDropContextProvider>
to the topmost react component.
Structure before:
App
Component1
Component2
...
ComponentX with render(<DragDropContextProvider>...<DragDropContextProvider>)
Structure after:
App with render(<DragDropContextProvider>...<DragDropContextProvider>)
Component1
Component2
...
ComponentX
Supposing App is loaded only once and HTML5 backend is not replicated.
You can either create a new file and import DragDropContext where you need it:
import HTML5 from 'react-dnd-html5-backend';
import {DragDropContext} from 'react-dnd';
export default DragDropContext(HTML5);
Source: https://github.com/react-dnd/react-dnd/issues/708#issuecomment-309259695
Or using Hooks:
import { DndProvider, createDndContext } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import React, { useRef } from "react";
const RNDContext = createDndContext(HTML5Backend);
function useDNDProviderElement(props) {
const manager = useRef(RNDContext);
if (!props.children) return null;
return <DndProvider manager={manager.current.dragDropManager}>{props.children}</DndProvider>;
}
export default function DragAndDrop(props) {
const DNDElement = useDNDProviderElement(props);
return <React.Fragment>{DNDElement}</React.Fragment>;
}
Use:
import DragAndDrop from "../some/path/DragAndDrop";
export default function MyComp(props){
return <DragAndDrop>....<DragAndDrop/>
}
Source: https://github.com/react-dnd/react-dnd/issues/186#issuecomment-561631584
If you use BrowserRouter then move the DragDropContextProvider out of BrowserRouter:
Source: https://github.com/react-dnd/react-dnd/issues/186#issuecomment-453990887
DndProvider has a options prop in where you can set rootElement which bounds DnD to that specified context, and unfortunately it isn't documented well. This approach solved all my issues, as I had other component which was using DnD and they were out of my boundary and I wasn't able to make HTML5Backend Singleton.
I tried this approach with "react-dnd": "^14.0.2"
const myFirstId = 'first-DnD-Containier';
const mySecondId = 'second-DnD-Containier';
export const DndWrapper = React.memo((props) => {
const [context, setContext] = useState(null);
useEffect(() => {
setContext(document.getElementById(props.id))
},[props.id])
return context ? (
<DndProvider backend={HTML5Backend} options={{ rootElement: context}}>
{props.children}
</DndProvider>
) : null;
});
export default function App() {
return (
<div>
<div id={myFirstId}>
<DndWrapper id={myFirstId}>
<MyOtherComponents />
</DndWrapper>
</div>
<div id={mySecondId}>
<DndWrapper id={mySecondId}>
<MyOtherComponents />
</DndWrapper>
</div>
</div>
);
}
P.S. Make sure by the time you call document.getElementById the DOM exist with ids.
Move the DragDropContextProvider out of BrowserRouter.
import { BrowserRouter as Router } from 'react-router-dom';
import { DndProvider } from 'react-dnd';
<DndProvider>
<Router>
<App />
</Router>
</DndProvider>

How can I use props to auto-populate editable redux-form fields in React?

I'm new to React so I've tried to show as much code as possible here to hopefully figure this out! Basically I just want to fill form fields with properties from an object that I fetched from another API. The object is stored in the autoFill reducer. For example, I would like to fill an input with autoFill.volumeInfo.title, where the user can change the value before submitting if they want.
I used mapDispatchtoProps from the autoFill action creator, but this.props.autoFill is still appearing as undefined in the FillForm component. I'm also confused about how to then use props again to submit the form. Thanks!
My reducer:
import { AUTO_FILL } from '../actions/index';
export default function(state = null, action) {
switch(action.type) {
case AUTO_FILL:
return action.payload;
}
return state;
}
Action creator:
export const AUTO_FILL = 'AUTO_FILL';
export function autoFill(data) {
return {
type: AUTO_FILL,
payload: data
}
}
Calling the autoFill action creator:
class SelectBook extends Component {
render() {
return (
....
<button
className="btn btn-primary"
onClick={() => this.props.autoFill(this.props.result)}>
Next
</button>
);
}
}
....
function mapDispatchToProps(dispatch) {
return bindActionCreators({ autoFill }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(SelectBook);
And here is the actual Form where the issues lie:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';
import { createBook } from '../actions/index;
class FillForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
}
onSubmit(props) {
this.props.createBook(props)
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
const { fields: { title }, handleSubmit } = this.props;
return (
<form {...initialValues} onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<input type="text" className="form-control" name="title" {...title} />
<button type="submit">Submit</button>
</form>
)
}
export default reduxForm({
form: 'AutoForm',
fields: ['title']
},
state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
}), {createBook})(FillForm)
I think you're mixing up connect and reduxForm decorators in the actual form component. Currently your code looks like this (annotations added by me):
export default reduxForm({
// redux form options
form: 'AutoForm',
fields: ['title']
},
// is this supposed to be mapStateToProps?
state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
}),
/// and is this mapDispatchToProps?
{createBook})(FillForm)
If this is the case, then the fix should be as simple as using the connect decorator as it should be (I also recommend separating this connect props to their own variables to minimize confusions like this):
const mapStateToProps = state => ({
initialValues: {
title: state.autoFill.volumeInfo.title
}
})
const mapDispatchToProps = { createBook }
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm({ form: 'AutoForm', fields: ['title'] })(FillForm)
)
Hope this helps!

Dynamically include files (components) and dynamically inject those components

Looking around the next I could not find the answer: How do I dynamicly include a file, based on prop change per say: here some sudo code to intrastate what I'm trying to do!
class Test extends React.Component {
constructor(props){
super(props)
this.state = { componentIncluded: false }
includeFile() {
require(this.props.componetFileDir) // e.g. ./file/dir/comp.js
this.setState({ componentIncluded: true });
}
render() {
return(
<div className="card">
<button onClick={this.includeFile}> Load File </button>
{ this.state.componentIncluded &&
<this.state.customComponent />
}
</div>
)
}
}
so this.props.componetFileDir has access to the file dir, but I need to dynamically include it, and can't really do require() as its seems to running before the action onClick get called. Any help would be great.
Em, Your code looks a bit wrong to me. So I created a separate demo for dynamic inject components.
While in different situation you can use different React lifecycle functions to inject your component. Like componentWillReceiveProps or componentWillUpdate.
componentDidMount() {
// dynamically inject a Button component.
System.import('../../../components/Button')
.then((component) => {
// update the state to render the component.
this.setState({
component: component.default,
});
});
}
render() {
let Button = null;
if (this.state.component !== null) {
Button = this.state.component;
}
return (
<div>
{ this.state.component !== null ? <Button>OK</Button> : false }
</div>
);
}
After you edited your code, it should be something similar to below:
class Test extends React.Component {
constructor(props){
super(props)
this.state = { customComponent: null }
this.includeFile = this.includeFile.bind(this);
}
includeFile() {
System.import(this.props.componetFileDir)
.then((component) => {
this.setState({ customComponent: component.default });
});
}
render() {
return(
<div className="card">
<button onClick={this.includeFile}> Load File </button>
{
this.state.customComponent
}
</div>
)
}
}

React input defaultValue focus and select

I have a component with an input-element, i use defaultValue to set the initial value.
I want to focus that element and select the whole value initially, but it seems that the defaultvalue is not set when componentDidMount is called.
Do you have any tips?
i use window.setTimeout but i want to avoid that in my react-components:
public componentDidMount(): void {
if (this.props.focus) {
let tInput: HTMLInputElement = ReactDOM.findDOMNode(this).getElementsByTagName("input").item(0);
if (tInput) {
tInput.focus();
// FixMe: defaultValue is set too late by react so i cant set selection instantly
if (this.props.defaultValue) {
window.setTimeout(
() => {tInput.setSelectionRange(0, this.props.defaultValue.length); },
100
);
}
}
}
}
Works fine for me:
class MyComponent extends React.Component {
componentDidMount () {
const { myInput } = this.refs
myInput.focus()
myInput.select()
}
render () {
return (
<input type='text' defaultValue='Foobar' ref='myInput' />
)
}
}
I wouldn't use any reactDOM methods other than render/renderToString. These apis are "escape hatches" and usage is not recommended.