How to modify styles within the draftjs editor? - reactjs

I'm tring to use draftjs and want to crate a custom block component.
First question is, I can't perfectly complete an example which is create in document (link here).
When I click the button named 'bold', the editor loses focus and my text doesn't get bolder.
Here is my code:
import React, { Component } from 'react';
import { Editor, EditorState, RichUtils } from 'draft-js';
import { Paper, Button } from '#material-ui/core';
class ProductComponent extends Component {
render() {
const { src } = this.props;
return (
<div>{src}</div>
)
}
}
export default class MyEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorState: EditorState.createEmpty()
}
}
onChange = (editorState) => this.setState({ editorState });
onBoldClick = () => {
this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD'));
}
myBlockStyleFn = (contentBlock) => {
const type = contentBlock.getType();
if (type === 'product') {
return {
component: ProductComponent,
editable: false,
props: {
src: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3307626454,3432420457&fm=27&gp=0.jpg',
},
}
}
}
render() {
const {editorState} = this.state;
return (
<Paper elevation={0}>
<Button onClick={this.onBoldClick}>bold</Button>
<Editor
blockStyleFn={this.myBlockStyleFn}
editorState={editorState}
onChange={this.onChange}
/>
</Paper>
);
}
}

Related

Changing state between two child class components in React

I have two class components: Title and PlayButton. By default, the Title is programmed to change images when it is being hovered over but I would like it so that when the Playbutton is hovered over, the Title also changes its image (changes the state of the image). How would I go about this? I know I should use a parent component that handles the state of both its "children" (the Title and the PlayButton), but since I'm new to react, I'm not sure how.
Any assistance would be appreciated, thank you!
Code for Title:
import React from 'react'
import './Title.css'
import playHoverProvider from './playHoverProvider'
class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./oglogo'),
control: require('./oglogo')
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
handleMouseOver() {
this.setState({
imgSrc: require('./difflogo')
});
}
handleMouseOut() {
this.setState({
imgSrc: require('./oglogo')
});
}
render() {
return (
<div className='logo'>
<view>
<img onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} src={this.state.imgSrc}
style={{width: 800,
flex: 1,
height: null,
}}
alt = 'Logo' />
</view>
</div>
);
}
}
Title.propTypes = {
}
Title.defaultProps = {
}
export default Title;
Code for PlayButton:
import { hover } from '#testing-library/user-event/dist/hover';
import React from 'react'
import './PlayButton.css';
class PlayButton extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./playbutton.png'),
disabled: false
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
// On-click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({
disabled: true
});
}
handleMouseOver () {
this.setState({
imgSrc: require('./playbuttonblue.png')
});
}
handleMouseOut () {
this.setState({
imgSrc: require('./playbutton.png')
});
}
render() {
return (
<div className='playbutton'>
<a href='./Rule'>
<button className='buttonprop' onClick={this.handleClick} disabled={this.state.disabled}>
{this.state.disabled ? '' :
<img onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
src={this.state.imgSrc} width = {100} height = {50} alt = 'Play'/>}
</button>
</a>
</div>
);
}
}
PlayButton.propTypes = {
}
PlayButton.defaultProps = {
}
export default PlayButton

how to displayed local content react-draft-wysiwyg

how do i display edited content with all styles?
const content = {
entityMap: {},
blocks: [
{
key: "637gr",
text: "Initialized from content state.",
type: "unstyled",
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
}
export default class EditorConvertToJSON extends Component {
constructor(props) {
super(props)
const contentState = convertFromRaw(content)
this.state = {
contentState,
}
}
onContentStateChange = (contentState) => {
this.setState({
contentState,
})
}
render() {
const { contentState } = this.state
console.log("==============")
console.log("contentState", contentState)
return (
<div>
<Editor
wrapperClassName="demo-wrapper"
editorClassName="demo-editor"
onContentStateChange={this.onContentStateChange}
// editorState={this.state.contentState}
/>
<Editor editorState={contentState} readOnly />
</div>
)
}
}
I get an error TypeError: editorState.getImmutable is not a function
where am I wrong?
may need to display this data in divs and other html tags?
I'm completely confused
I hope this helps you:
Do
npm i draftjs-to-html
const content = {
entityMap: {},
blocks: [
{
key: "637gr",
text: "Initialized from content state.",
type: "unstyled",
depth: 0,
inlineStyleRanges: [],
entityRanges: [],
data: {},
},
],
}
export default class EditorExample extends Component {
constructor(props) {
super(props)
this.state = {
contentState : null
}
}
onContentStateChange = contentState => {
console.log('as HTML:', draftToHtml(contentState));
this.setState({ contentState});
}
render() {
const { contentState } = this.state
return (
<Editor
initialContentState={content}
editorContent={contentState}
onContentStateChange={this.onContentStateChange}
wrapperClassName="demo-wrapper"
editorClassName="demo-editor"
/>
)
}
}
Here is the full example from official documentation of react-draft-wyswiyg you can find example if you scroll down to the bottom of documentation web page :). Here you need to use convertToRaw method of draft-js. With the help of draftjs-to-html, code will be similar to draftToHtml(convertToRaw(editorState.getCurrentContent()))
import React, { Component } from 'react';
import { EditorState, convertToRaw, ContentState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
import draftToHtml from 'draftjs-to-html';
import htmlToDraft from 'html-to-draftjs';
class EditorConvertToHTML extends Component {
constructor(props) {
super(props);
const html = '<p>Hey this <strong>editor</strong> rocks 😀</p>';
const contentBlock = htmlToDraft(html);
if (contentBlock) {
const contentState = ContentState.createFromBlockArray(contentBlock.contentBlocks);
const editorState = EditorState.createWithContent(contentState);
this.state = {
editorState,
};
}
}
onEditorStateChange: Function = (editorState) => {
this.setState({
editorState,
});
};
render() {
const { editorState } = this.state;
return (
<div>
<Editor
editorState={editorState}
wrapperClassName="demo-wrapper"
editorClassName="demo-editor"
onEditorStateChange={this.onEditorStateChange}
/>
<textarea
disabled
value={draftToHtml(convertToRaw(editorState.getCurrentContent()))}
/>
</div>
);
}
}

react change slide in slider after clicking a button

I have a Sidebar that I've implemented this way:
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
Then I have a component called ProjectSliderComponent that realizes the behaviour of a carousel. It is done this way:
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
render() {
let i = 0;
return (
<div>
<Slider ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}
The ProjectComponent component simply specifies the layout. This is the App.js file, where I load the projectslidercomponent and my sidebar. I do this:
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
render() {
return (
<div className="App">
<MySidebar>
</MySidebar>
<ProjectSliderComponent projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
What I need to do is to change the slide according to which button I clicked. What do I have to do? Is there something like passing the "instance" of the projectslidercomponent and call a method to change the slide? Or something else?
At the react-slick docs you can read about slickGoTo, which is a method of the Slider component.
Since you're already storing this.slider you can try to make it accessible in MySidebar and use it whenever changeSlide is called. Most likely you have to create a changeSlide method on top level and hand it as property to your components.
To sort this problem what you have to do is create a function in parent component which updates the state of the app component and once the state is updated it will re render app component and the new props are send to slider component which will tell which slider to show. Below is the code :-
In App.js
class App extends Component {
state = {
projectList: new ProjectListModel(),
isLoading: true,
menuId: 0
}
constructor(props) {
super(props);
this.logger = new LoggerService();
}
componentDidMount() {
let projectService = new ProjectService();
projectService.getProjectList()
.then(res => {
let projectList = new ProjectListModel();
projectList.data = res.data.data;
this.setState({ projectList: projectList,
isLoading: false });
})
.catch(error => {
this.logger.error(error);
});
}
ChangeSlide = (menuId) => {
this.setState({
menuId //Here you will receive which slide to show and according to that render slides in mySliderbar component
})
}
render() {
return (
<div className="App">
<MySidebar ChangeSlide={this.ChangeSlide} />
<ProjectSliderComponent menuId={this.state.menuId} projectListId={this.state.projectList}></ProjectSliderComponent>
</div>
);
}
}
export default App;
In mySlidebar
import React from "react";
import './MySidebar.scss';
import {slide as Menu } from 'react-burger-menu'
export class MySidebar extends React.Component {
constructor(props) {
super(props);
}
changeSlide = (e) => {
console.log("Clicked " + e.currentTarget.id); //get text content of
this.props.changeSlide(e.currentTarget.id)
}
render() {
return (
<Menu customCrossIcon={false}>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project1">Project 1</button>
<button onClick={((e) => this.changeSlide(e))} className ="menu-item" id="Project2">Project 2</button>
</Menu>
);
}
}
In slider component, you have to listen when the new props are coming and according to that change the slide see componentWillReceiveProps
import React from "react";
import Slider from "react-slick";
import './project-carousel.component.css';
import { ProjectComponent } from '../project/project.component';
import { LoggerService } from '../../services/logger-service';
import { appConfig } from '../../config';
export class ProjectSliderComponent extends React.Component {
state = {
activeSlide: 0,
timestamp: Date.now()
}
constructor(props) {
super(props);
this.logger = new LoggerService();
this.settings = {
dots: appConfig.dots,
arrows: false,
adaptiveHeight: true,
infinite: appConfig.infinite,
speed: appConfig.speed,
autoplay: appConfig.autoplay,
autoplaySpeed: appConfig.autoplaySpeed,
slidesToShow: 1,
slidesToScroll: 1,
mobileFirst: true,
className: 'heliosSlide',
beforeChange: (current, next) => {
this.setState({ activeSlide: next, timestamp: Date.now() });
}
};
}
componentWillReceiveProps(nextProps) {
if(nextprops.menuId != this.props.menuId){
this.slider.slickGoTo(nextprops.menuId, true)//use slider instance to
//call the function to go to a particular slide.
}
}
render() {
let i = 0;
const { menuId } = this.props
return (
<div>
<Slider initialSlide={menuId} ref = {c => (this.slider = c)} {...this.settings}>
{
this.props.projectListId.data.map(projectId =>
<ProjectComponent key={projectId} id={projectId} time={this.state.timestamp} originalIndex={ i++ } currentIndex = {this.state.activeSlide}></ProjectComponent>)
}
</Slider>
</div>
);
}
}

React native, delay api call?

I have a method called: onChangeText
It means every time I type, it will search the remote api.
How do I delay the remote api call? i.e. let user types certain things, then connect the api, rather than connect every key stroke.
onChangeText(title) {
console.log('-- chg text --');
console.log(title);
this.props.searchApi(title);
}
The component:
import React, { Component } from 'react';
import { SearchBar, Divider } from 'react-native-elements';
import { View, ScrollView, Text, StyleSheet, Image} from 'react-native';
import { connect } from 'react-redux';
// action creator
import { searchApi } from './reducer';
class SearchContainer extends Component {
constructor(props) {
super(props);
}
onChangeText(title) {
console.log('-- chg text --');
console.log(title);
this.props.searchApi(title);
}
onClearText(e) {
console.log('-- clear text --');
console.log(e);
}
render() {
const { } = this.props;
const containerStyle = {
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
const searchStyle = {
width: 300,
height: 45
};
return (
<View
style={containerStyle}
>
<Image
source={require('../../asset/img/logo.png')}
style={{
height: 150,
width: 150
}}
/>
<SearchBar
cancelButtonTitle="Cancel"
placeholder='Search'
containerStyle={searchStyle}
onChangeText={this.onChangeText.bind(this)}
onClearText={this.onClearText.bind(this)}
/>
</View>
);
}
}
const mapStateToProps = state => {
return {
};
};
const mapDispatchToProps = dispatch => {
return {
searchApi: () => dispatch(searchApi())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(SearchContainer);
Use lodash debounce. It is used for this exact use case
Sample React example. Should be able to port to native the same way
import React, {Component} from 'react'
import { debounce } from 'lodash'
class TableSearch extends Component {
//********************************************/
constructor(props){
super(props)
this.state = {
value: props.value
}
this.changeSearch = debounce(this.props.changeSearch, 250)
}
//********************************************/
handleChange = (e) => {
const val = e.target.value
this.setState({ value: val }, () => {
this.changeSearch(val)
})
}
//********************************************/
render() {
return (
<input
onChange = {this.handleChange}
value = {this.props.value}
/>
)
}
//********************************************/
}

Modal state with react/redux

I'm managing Todo lists in my app. The main view is a page with all the lists displayed as cards. If you click on one of them, you can modify, update, delete stuff through a modal that appears.
I have a TodoLists reducer that store all the TodoLists. I don't know how to handle the modal. Should I use redux or just local state?
import _ from "lodash";
import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { listsActions } from "../duck";
import NewList from "./NewList";
import Card from "./Card";
import Modal from "./Modal";
class Lists extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
list: {}
};
this.hideModal = this.hideModal.bind(this);
this.renderModal = this.renderModal.bind(this);
}
componentDidMount() {
const { fetchByUserId, user } = this.props;
if (user !== undefined) {
fetchByUserId(user.id);
}
}
hideModal() {
this.setState({
modal: false
});
}
renderModal() {
this.setState({
modal: true
});
}
render() {
const { items } = this.props;
const { modal, list } = this.state;
return (
<div>
<NewProject />
<div className="columns">
{_.map(items, (l) => (
<div
key={l.id}
className="column"
>
<Card
list={l}
onClick={() => this.renderModal(l)}
/>
</div>
))}
</div>
<Modal
className={modal ? "is-active" : ""}
list={list}
onClose={this.hideModal}
/>
</div>
);
}
}
const mapStateToProps = (state) => {
const { user } = state.authentication;
const { items, loading, error } = state.lists;
return {
user,
items,
loading,
error
};
};
export default connect(
mapStateToProps,
{ fetchByUserId: listsActions.fetchByUserId }
)(Projects);

Resources