Code involved using DraftJS and Meteor Js application
Task - Make a live preview where text from DraftJS will get saved to DB and from DB it get displayed on another component.
But problem is once data comes from DB and I try to edit DraftJS cursor moved to the beginning.
Code is
import {Editor, EditorState, ContentState} from 'draft-js';
import React, { Component } from 'react';
import { TestDB } from '../api/yaml-component.js';
import { createContainer } from 'meteor/react-meteor-data';
import PropTypes from 'prop-types';
class EditorComponent extends Component {
constructor(props) {
super(props);
this.state = {
editorState : EditorState.createEmpty(),
};
}
componentWillReceiveProps(nextProps) {
console.log('Receiving Props');
if (!nextProps) return;
console.log(nextProps);
let j = nextProps.testDB[0];
let c = ContentState.createFromText(j.text);
this.setState({
editorState: EditorState.createWithContent(c),
})
}
insertToDB(finalComponentStructure) {
if (!finalComponentStructure) return;
finalComponentStructure.author = 'Sandeep3005';
Meteor.call('testDB.insert', finalComponentStructure);
}
_handleChange(editorState) {
console.log('Inside handle change');
let contentState = editorState.getCurrentContent();
this.insertToDB({text: contentState.getPlainText()});
this.setState({editorState});
}
render() {
return (
<div>
<Editor
placeholder="Insert YAML Here"
editorState={this.state.editorState}
onChange={this._handleChange.bind(this)}
/>
</div>
);
}
}
EditorComponent.propTypes = {
staff: PropTypes.array.isRequired,
};
export default createContainer(() => {
return {
staff: Staff.find({}).fetch(),
};
}, EditorComponent);
Any helpful comment in right direction will be useful
When you call EditorState.createWithContent(c) Draft will return a new EditorState for you, but it has no idea about your current SelectionState. Instead, it will just create a new empty selection in the first block of your new ContentState.
To overcome this, you will have to set the SelectionState yourself, using the SelectionState from your current state, e.g:
const stateWithContent = EditorState.createWithContent(c)
const currentSelection = this.state.editorState.getSelection()
const stateWithContentAndSelection = EditorState.forceSelection(stateWithContent, currentSelection)
this.setState({
editorState: stateWithContentAndSelection
})
There is a propery to move focus to end:
const newState = EditorState.createEmpty()
this.setState({
editorState:
EditorState.moveFocusToEnd(newState)
})
This works for me.
All you need to do is to pass your given EditorState inside built in static EditorState.moveSelectionToEnd() method:
const editorState = EditorState.createEmpty();
const editorStateWithFocusOnTheEnd = EditorState.moveSelectionToEnd(editorState)
Related
There is a similar question but I can't comment on it so I opening a new one.
I am new to React and try to implement React SpeechRecognition component for my app. The text should be in an input box. the code for it (from react doc [https://www.npmjs.com/package/react-speech-recognition][1] - with span tag instead of an input):
import React, { PropTypes, Component } from 'react'
import SpeechRecognition from 'react-speech-recognition'
const propTypes = {
// Props injected by SpeechRecognition
transcript: PropTypes.string,
resetTranscript: PropTypes.func,
browserSupportsSpeechRecognition: PropTypes.bool
}
class Dictaphone extends Component {
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props
if (!browserSupportsSpeechRecognition) {
return null
}
return (
<div>
<button onClick={resetTranscript}>Reset</button>
<span>{transcript}</span>
</div>
)
}
}
Dictaphone.propTypes = propTypes
export default SpeechRecognition(Dictaphone)
Now I try to update a state of text (a string) by the transcript (the words that have been already recognized) but I can't make it.
from an earlier question, someone suggested this:
<input
type="text"
value={transcript}
onChange={event => this.onInputChange(event.target.value)}
/>
now when I speak, I do see the words in the input box,
so the final code should be :
import React, { Component } from "react";
import PropTypes from "prop-types";
import SpeechRecognition from "react-speech-recognition";
const propTypes = {
// Props injected by SpeechRecognition
transcript: PropTypes.string,
resetTranscript: PropTypes.func,
browserSupportsSpeechRecognition: PropTypes.bool
};
class Dictaphone extends Component {
constructor() {
super();
this.state = {
text: '',
events: []
}
}
onInputChange = (event) => {
console.log (event.target.value);
this.setState( {text: event.target.value} );
}
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props;
if (!browserSupportsSpeechRecognition) {
return null
}
return (
<div>
<button onClick={resetTranscript}>Reset</button>
<input
className='bg-light-blue'
type="text"
value={transcript}
onChange={event => this.onInputChange(event.target.value)}
/>
</div>
)
}
}
Dictaphone.propTypes = propTypes;
export default SpeechRecognition(Dictaphone);
but when I console.log(event.target.value) which is text - I see nothing so I'm doing something wrong.
Note that if I just write in the render func:
render() {
const { transcript, resetTranscript, browserSupportsSpeechRecognition } = this.props;
var x = transcript;
console.log('x is ',x);
console.log('x length is: ',x.length);
.....
it does console the transcript (x) but it's not what I want - I need to save it in text by updating the state.
any suggestion?
If you need to store the transcript prop in your state you should do something like this.
componentDidUpdate(prevProps){
if(prevProps.transcript !== this.props.transcript){
this.setState({
text: this.props.transcript
});
}
}
In your render method use this.state.text to show in the input value.
Also in your constructor do
this.state = {
text: props.transcript
}
I have been learning how to use React recently and have been taking an online course to do so. I have been having a similar problem as found here TypeError: this.state.robots.filter is not a function? but none of the solutions seem to work. Essentially the code should pull information from an API and the function in question should filter it depending on the input in the search bar. The terminal says its running successfully but when I check the website I get this response:
Error displayed on webpage
Below is the code I am using
import React from 'react';
import CardList from './CardList';
import SearchBox from './SearchBox';
import './App.css';
class App extends React.Component {
constructor() {
super()
this.state = {
robots: {},
searchfield: '',
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response=> {
return response.json();
})
.then(users => {
this.setState ({robots: users})
});
}
onSearchChange = (event) => {
this.setState({ searchfield: event.target.value})
};
render() {
const filterRobots = this.state.robots.filter (robots => {
return robots.name.toLowerCase().includes(this.state.searchfield.toLowerCase());
})
return (
<div className= 'tc '>
<h1>Robo-Friends</h1>
<SearchBox searchChange={this.onSearchChange}/>
<CardList robots={filterRobots}/>
</div>
);
}
}
export default App
Any help would be much appreciated and thank you in advance.
filter is a function on array. Your robots variable in state is an object, hence the error says filter does not exist on it. Define it as an empty array in state like so:
this.state = {
robots: []
}
I am assuming that robots is an array, not an object, since you are trying to filter/loop through it. Hence, the initial state of robots should be defined as an empty array([]).
constructor() {
super()
this.state = {
robots: [],
searchfield: '',
};
}
Side note, you can clean up your filter statement by destructuring the state.
const { robots, searchfield } = this.state;
const filterRobots = robots.filter(({ name }) => {
return name.toLowerCase().includes(searchfield.toLowerCase());
})
I use markdown-to-jsx to render markdown in my React component.
My problem is that I want to dynamically load the markdown file, instead of specifying it with import. The scenario is that this happens on an article details page, i.e. I get the articleId from the route params and then based on that id, I want to load the corresponding markdown file, e.g. article-123.md.
Here's what I have so far. How can I load the md file dynamically?
import React, { Component } from 'react'
import Markdown from 'markdown-to-jsx';
import articleMd from './article-123.md'
class Article extends Component {
constructor(props) {
super(props)
this.state = { md: '' }
}
componentWillMount() {
fetch(articleMd)
.then((res) => res.text())
.then((md) => {
this.setState({ md })
})
}
render() {
return (
<div className="article">
<Markdown children={this.state.md}/>
</div>
)
}
}
export default Article
This works fine as is, but if I remove import articleMd from './article-123.md' at the top and instead pass the file path directly to fetch it output what looks like index.html, not the expected md file.
Can't you use dynamic import?
class Article extends React.Component {
constructor(props) {
super(props)
this.state = { md: '' }
}
async componentDidMount() {
const articleId = this.props.params.articleId; // or however you get your articleId
const file = await import(`./article-${articleId}.md`);
const response = await fetch(file.default);
const text = await response.text();
this.setState({
md: text
})
}
render() {
return (
<div className="article">
<Markdown children={this.state.md} />
</div>
)
}
}
I know this is an old thread but I just solved this issue with the following code
using markdown-to-jsx
import React, { Component } from 'react'
import Markdown from 'markdown-to-jsx'
class Markdown_parser extends Component {
constructor(props) {
super(props)
this.state = { md: "" }
}
componentWillMount() {
const { path } = this.props;
import(`${path}`).then((module)=>
fetch(module.default)
.then((res) => res.text())
.then((md) => {
this.setState({ md })
})
)
}
render() {
let { md } = this.state
return (
<div className="post">
<Markdown children={md} />
</div>
)
}
}
export default Markdown_parser
I then call the class sa follows
<Markdown_parser path = "path-to-your-fle" />
Components ->
Box
Todolist
Add
AddModal
Main component App
But it is not working that is when I add a new task. It does not get added properly.
I think I cannot use this.setstate twice in a function.
Hope I am correct
Here is given the main component.
App.js :
import React, { Component } from 'react';
import './App.css';
import Box from './Components/Box';
import Add from './Components/Add';
import Todolist from './Components/Todolist';
class App extends Component {
constructor(props) {
super(props);
this.state = {
lists: '',
inputValue: '',
itemArray: []
}
}
onAddTask = () => {
this.setState ({
lists: this.state.inputValue
});
const item = this.state.itemArray;
const title = this.state.lists;
item.push({ title })
this.setState(prevState => ({
itemArray: [...prevState.lists, title]
}))
}
updateInputValue = (event) => {
this.setState({
inputValue: event.target.value
});
}
render() {
let length = this.state.itemArray.length;
return (
<div className="App">
<Box createTodo = {
<div>
{this.state.itemArray.map((itemArr) => {
return (
<div className="box">
<Todolist tasks = {itemArr} />
</div>
)
})}
</div>
}>
</Box>
<Add addTask = {this.onAddTask} inputValues = {this.updateInputValue} inputV = {this.state.inputValue} />
</div>
);
}
}
export default App;
Your addTasks function is not correct, you are mixing up things here.
In your inputValue you save the current value from the input field right? So if you write the following
this.setState({
lists: this.state.inputValue
});
you set your todo list to this single value. And your todo list is not an array anymore.
Secondly, state is imutable. So if you write the following
this.state.itemArray.push({ title });
the state will not be updated. What you actually want is the following:
onAddTask = () => {
this.setState({
itemArray: [...this.state.itemArray, this.state.inputValue]
})
}
And I'm not sure what the lists property on the state is for. You don't use it anywhere besides in your onAddTask function. So I guess you can remove it.
I'm making progress on this app. I'm able to access and render the list of ingredients now I need to do the same with the name of the recipe. Postman indicates that it is under recipes.body.matches[0].sourceDisplayName. I created another function, similar to what got me the ingredients. Getting the following error...
TypeError: Cannot read property 'map' of undefined
import React from 'react';
import Request from 'superagent';
import _ from 'lodash';
export class Yum extends React.Component {
constructor(){
super();
this.state = {
searchQuery: 'onion',
recipe: {
ingredients: []
}
};
this.search = this.search.bind(this);
this.queryUpdate = this.queryUpdate.bind(this);
}
componentWillMount(){
this.search(this.state.searchQuery);
}
render(){
//const title = 'Onion Soup'; // Get this from somwhere else ?
const {recipe, searchQuery} = this.state; // Get state properties
const displayName = _.get(recipe, 'sourceDisplayName').map((sourceDisplayName) => {
return (<h4>{displayName}</h4>)
});
const listItems = _.get(recipe, 'ingredients', []).map((ingredient, sourceDisplayName) => {
return (<h5>{ingredient}</h5>);
});
return(
<div>
<input onChange={this.queryUpdate} type="text" value={searchQuery} />
<h4>{displayName}</h4>
<ul>
<li>{listItems}</li>
</ul>
</div>
)
}
queryUpdate(event) {
const searchQuery = event.target.value; // Get new value from DOM event
this.setState({searchQuery}); // Save to state
this.search(searchQuery); // Search
}
search(searchQuery) {
const url = `http://api.yummly.com/v1/api/recipes?_app_id=5129dd16&_app_key=9772f1db10ba433223ad4e765dc2b537&q=${searchQuery}&maxResult=1`
Request.get(url).then((response) => {
this.setState({
recipe: response.body.matches[0]
});
});
}
}
export default Yum;
Any suggestions?