I am using better-react-mathjax for equation writing and reading. Basically, I used the MathJax in my equation with a question. When it first loads it does not create a problem.
But when I use to filter using React sate it creates the Typesetting problem and the next app is crashed.
How can I fix the problem?
MathJax
import React from 'react'
import { MathJax, MathJaxContext } from "better-react-mathjax";
const config = {
loader: { load: ["input/asciimath"] }
};
export const MathJaxOutput = ({ text }) => {
return <MathJaxContext config={config} version={3}>
<MathJax dynamic inline>
{text}
</MathJax>
</MathJaxContext>
}
And the error screenshot is
When mark and course name changed automatically loads the related questions.
state = {
courseName: '',
selectedTopics: [],
mark: null,
questions:[]
}
componentDidUpdate(prevProps, prevState) {
if (this.state.courseName !== prevState.courseName || this.state.selectedTopics !== prevState.selectedTopics || this.state.mark !== prevState.mark) {
if (this.state.courseName) {
this.props.getCourseQuestions(this.state.courseName, this.state.selectedTopics, this.state.mark);
}
}
}
Output render
{
this.state.questions.map((question, questionIndex) => (
<div className='input-question-field-items' key={questionIndex}>
<div className='preview-field-item'>
<MathJaxOutput text={<p>{question.questionInput.question}</p>} />
</div>
</div>
}
Related
Right now I'm building a DnD game.
It looks like this:
When I drop an answer over a slot, the answer should be in that exact place, so the order matters.
My approach kind of works, but not perfectly.
So, in the parent component I have two arrays which store the slots' content and the answers' content.
The problem is easy to understand, you don't have to look at the code in-depth, just mostly follow my text :)
import DraggableGameAnswers from './DraggableGameAnswers';
import DraggableGameSlots from './DraggableGameSlots';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { useState } from 'react';
type DraggableGameStartProps = {
gameImages: Array<string>,
gameAnswers: Array<string>,
numberOfGameAnswers?: number,
typeOfAnswers: string,
correctAnswer: string
}
function DraggableGameStart(props: DraggableGameStartProps) {
const [slots, setSlots] = useState<string[]>(["", "", "", ""]);
const [answers, setAnswers] = useState<string[]>(props.gameAnswers);
return (
<DndProvider backend={HTML5Backend}>
<div className="draggable-game-content">
<div className="draggable-game">
<DraggableGameSlots
answers={answers}
slots={slots}
setAnswers={setAnswers}
setSlots={setSlots}
numberOfAnswers={4}
slotFor={props.typeOfAnswers}
/>
<DraggableGameAnswers
gameAnswers={answers}
numberOfGameAnswers={props.numberOfGameAnswers}
typeOfAnswers={props.typeOfAnswers}
/>
</div>
</div>
</DndProvider>
);
}
export default DraggableGameStart;
DraggableGameSlots is then a container, which loops through props.slots and renders each slot.
import React, { useState } from "react";
import DraggableGameSlot from "./DraggableGameSlot";
type DraggableGameSlotsProps = {
answers: string[],
slots: string[],
setAnswers: any,
setSlots: any,
numberOfAnswers: number,
slotFor: string
}
function DraggableGameSlots(props: DraggableGameSlotsProps) {
return (
<div className="draggable-game-slots">
{
props.slots.map((val, index) => (
<DraggableGameSlot
index={index}
typeOf={props.slotFor === "text" ? "text" : "image"}
key={index}
answers={props.answers}
slots={props.slots}
setAnswers={props.setAnswers}
setSlots={props.setSlots}
toDisplay={val === "" ? "Drop here" : val}
/>
))
}
</div>
);
}
export default DraggableGameSlots;
In DraggableGameSlot I make my slots a drop target. When I drop an element, I iterate through the slots, and when I reach that element's position in the array, I modify it with the answer.
Until now everything works fine, as expected, I just wrote this for context.
E.g. if the drop target's index is 2, I modify the 3rd position in the slots array. (slots2 for 0-index based)
import { useEffect } from 'react';
import { useDrop } from 'react-dnd';
import './css/DraggableGameSlot.css';
import DraggableGameImage from './DraggableGameImage';
type DraggableGameSlotProps = {
index: number,
typeOf: string,
answers: string[],
slots: string[],
setAnswers: any,
setSlots: any,
toDisplay: string
}
function DraggableGameSlot(props: DraggableGameSlotProps) {
const [{ isOver }, drop] = useDrop(() => ({
accept: "image",
drop(item: { id: string, toDisplay: string }) {
props.setAnswers(props.answers.filter((val, index) => index !== parseInt(item.id)));
props.setSlots(props.slots.map((val, index) => {
if (props.index === index) {
console.log("ITEM " + item.toDisplay);
return item.toDisplay;
}
return val;
}));
},
collect: (monitor) => ({
isOver: !!monitor.isOver(),
})
}), [props.slots])
// useEffect(() => console.log("answers " +props.answers), [props.answers]);
// useEffect(() => console.log("slots " + props.slots), [props.slots]);
const dragClass: string = isOver ? "is-dragged-on" : "";
return (
<>
{(props.typeOf === "image" && props.toDisplay !== "Drop here") ?
<>
<span>da</span>
<DraggableGameImage
className={`game-answers-image ${dragClass}`}
imgSrc={props.toDisplay}
imgAlt={"test"}
dndRef={drop}
/>
</>
:
<div className={`draggable-game-slot draggable-game-slot-for-${props.typeOf} ${dragClass}`} ref={drop}>
<span>{props.toDisplay}</span>
</div>
}
</>
)
}
export default DraggableGameSlot;
The problem comes with the logic in DraggableGameAnswer.
First, the container, DraggableGameAnswer. Using the loop, I pass that "type" (it is important because from there the error happens)
DraggableGameAnswers.tsx
import './css/DraggableGameAnswers.css';
import GameNext from '../GameComponents/GameNext';
import DraggableGameAnswer from './DraggableGameAnswer';
import { useEffect } from 'react';
type DraggableGameAnswersProps = {
gameAnswers: Array<string>,
numberOfGameAnswers?: number,
typeOfAnswers: string
}
function DraggableGameAnswers(props: DraggableGameAnswersProps) {
let toDisplay: Array<string>;
if(props.numberOfGameAnswers !== undefined)
{
toDisplay = props.gameAnswers.slice(props.numberOfGameAnswers);
}
else
{
toDisplay = props.gameAnswers;
}
useEffect(() => console.log(toDisplay));
return (
<div className="draggable-game-answers">
{
toDisplay.map((val, index) => (
<DraggableGameAnswer
key={index}
answer={val}
typeOfAnswer={props.typeOfAnswers}
type={`answer${index}`}
/>
))}
<GameNext className="draggable-game-verify" buttonText="Verify"/>
</div>
);
}
export default DraggableGameAnswers;
In DraggableGameAnswer, I render the draggable answer:
import DraggableGameImage from "./DraggableGameImage";
import "./css/DraggableGameAnswer.css";
import { useDrag } from "react-dnd";
import { ItemTypes } from "./Constants";
type DraggableGameAnswerProps = {
answer: string,
typeOfAnswer: string,
type: string
}
function DraggableGameAnswer(props: DraggableGameAnswerProps) {
const [{ isDragging }, drag] = useDrag(() => ({
type: "image",
item: { id: ItemTypes[props.type],
toDisplay: props.answer,
typeOfAnswer: props.typeOfAnswer },
collect: (monitor) => ({
isDragging: !!monitor.isDragging(),
})
}))
return (
<>
{props.typeOfAnswer === "image" ?
<DraggableGameImage
className="draggable-game-image-answer"
imgSrc={props.answer}
imgAlt="test"
dndRef={drag}
/> :
<div className="draggable-game-text-answer" ref={drag}>
{props.answer}
</div>
}
</>
);
}
export default DraggableGameAnswer;
Constants.tsx (for ItemTypes)
export const ItemTypes: Record<string, any> = {
answer0: '0',
answer1: '1',
answer2: '2',
answer3: '3'
}
Okay, now let me explain what's happening.
The error starts because of the logic in DraggableGameAnswers container - I set the type using the index for the loop.
Everything works fine at the beginning. Then, let's say, I move answer3 in in the 4th box. It looks like this now:
It is working fine until now.
But now, the components have re-rendered, the map runs again, but the array is shorter with 1 and answer1's type will be answer1, answer2's type will be answer2, and answer4's type will be answer3.
So, when I drag answer4 over any box, I will get another answer3, like this:
How can I avoid this behavior and still get answer4 in this situation? I'm looking for a react-style way, better than my idea that I'll describe below.
So, here is how I delete an answer from the answers array:
props.setAnswers(props.answers.filter((val, index) => index !== parseInt(item.id)));
I can just set the deleted's answer position to NULL, to keep my array length for that index, and when I render the answers if the value is NULL, just skip it.
I am trying to render the hex values in a file uploaded using react.
When I upload big files, say 10MB, the page crashes, but sites like http://mikach.github.io/hex-editor/ works like a charm. I don't understand what am I doing wrong.
Below is the code which does the same
import React from "react";
class App extends React.PureComponent {
constructor(props) {
super(props);
this.fileReader = null;
this.state = {
filecontent: [],
decodingSection: [
{ type: "BigUint64", startIndex: "0", endIndex: "0" },
{ type: "BigUint64", startIndex: "0", endIndex: "0" }
]
};
}
handleFileRead = () => {
const typedArray = new Uint8Array(this.fileReader.result);
const untypedArrray = [];
const iii = typedArray.values();
while (true) {
const { value, done } = iii.next();
if (done) {
break;
}
const hexValue = value.toString(16);
untypedArrray.push(hexValue.length === 1 ? `0${hexValue}` : hexValue);
}
this.setState({
filecontent: untypedArrray
});
};
handleFileChosen = file => {
this.fileReader = new FileReader();
this.fileReader.onloadend = this.handleFileRead;
this.fileReader.readAsArrayBuffer(file);
};
render() {
return (
<div>
<input
type={"file"}
id={"file"}
onChange={e => this.handleFileChosen(e.target.files[0])}
/>
<br />
{this.state.filecontent.length > 0 && (
<div>No Bytes present {this.state.filecontent.length}</div>
)}
{this.state.filecontent.map(each => `${each} `)}
</div>
);
}
}
export default App;
One possible cause could be the map this.state.filecontent.map(each => '${each} ') in the render method, thought I'm not sure.
I slightly modified the code, so that it saves the whole character sequence into a string called contents (using the array's join method) and then renders it at once.
You can take a look and give it a try here. At least, it worked for me on a 10Mb file :)
I have this simple react app, where I fetch the Flickr public feed. So, I can scroll to the end of the page and new content is going to show. So I would like to scroll until there is nothing else new, and the app stops trying to load more content, because it has reached the last item of the list, which is not happening if I try to scroll (you can see that on the loading message). How can I fix this?
Check the code below:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import $ from "jquery";
import PhotoListItem from "./photoListItem";
import "./photoApp.css";
export default class PhotoApp extends Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
searchTerm: "cyanotype",
items: 8,
loadingState: false,
loadingMessage: ""
};
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint =
"https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: "jsonp",
data: { format: "json" },
success: function(data) {
this.setState({ photoList: data.items });
}.bind(this)
});
} catch (err) {
console.log(err);
}
};
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight - 20
) {
this.loadMoreItems();
}
});
};
/* code for infinite scroll */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({
loadingState: true,
loadingMessage: "Loading photos..."
});
setTimeout(() => {
this.setState(prevState => ({
items: prevState.items + 8,
loadingState: false,
loadingMessage: "No more photos to show."
}));
}, 1000);
};
render() {
console.log(this.state.photoList)
return (
<div className="appContainer" ref="iScroll">
<div className="appHeader">
<h1 className="headerTitle">
Welcome to Flickr Alternative Photography Feed!
</h1>
</div>
<div className="gridContainer">
{this.state.photoList
.slice(0, this.state.items)
.map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1];
const description = photo.description.split(/"/)[13];
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags}
/>
);
})}
</div>
<React.Fragment>
{this.state.loadingState ? (
<p className="loading">{this.state.loadingMessage}</p>
) : (
<p className="loading">{this.state.loadingMessage}</p>
)}
</React.Fragment>
</div>
);
}
}
LIVE EXAMPLE HERE
Thank you!
You could check if the item that you've loaded exceeds your items in your ajax request
/* code for infinite scroll */
loadMoreItems = () => {
// hasMore = data.items.length (you may want to rename this more appropriately)
if (this.state.loadingState || (this.state.items > this.state.hasMore)) {
// Do not load if there's no more items
return;
}
...
Your onInfiniteScroll doesn't have any code right now that checks whether it should load more items, it just blindly does. So: at the end of getPhotoList you probably want to check whether that's the last page of results and if so, do a setState({ exhausted: true }) or something similar, so you can check that value in your onInfiniteScroll and not do anything if you see this.state.exhausted === true.
I'm follow the steps of this dependencie:
http://jossmac.github.io/react-images/
And it isn't work. No picture showing and there is showing an error message:
index.js:2178 Warning: Failed prop type: The prop onClose is marked
as required in Lightbox, but its value is undefined
Here is my code:
import React, { Component } from "react";
import Lightbox from "react-images";
const URL_INTERIORES = "http://localhost:3001/interiores";
const LIGHTBOX_IMAGE_SET = [
{
src: "/images/int_02.jpg",
caption: "A forest",
// As an array
srcSet: ["/images/int_02.jpg", "/images/int_02.jpg"]
},
{
src: "/images/int_02.jpg",
// As a string
srcSet: "/images/int_02.jpg 1024w, /images/int_02.jpg 800w, /images/int_02.jpg 500w, /images/int_02.jpg 320w"
}
];
class Interiores extends Component {
render() {
const { open } = this.state;
return (
<div>
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={this.state.lightboxIsOpen}
onClickPrev={this.gotoPrevLightboxImage}
onClickNext={this.gotoNextLightboxImage}
onClose={this.closeLightbox}
/>
</div>
</div>
);
}
}
export default Interiores;
Does anybody know how to solve it? Tahnk you
Consider adding all the missing handlers & state in your class:
class Interiores extends Component {
state = {
lightboxIsOpen: false
}
gotoPrevLightboxImage() {
// Add the logic here
}
gotoNextLightboxImage() {
// Add the logic here
}
closeLightbox(e) {
// Add the logic here
}
render() {
const { lightboxIsOpen } = this.state;
return (
<div>
<Lightbox
images={LIGHTBOX_IMAGE_SET}
isOpen={lightboxIsOpen}
onClickPrev={() => this.gotoPrevLightboxImage()}
onClickNext={() => this.gotoNextLightboxImage()}
onClose={e => this.closeLightbox(e)}
/>
</div>
);
}
}
I'm currently creating a custom React component in Meteor for adding images to a list (and later uploading them). However when I try to delete images from the list, always the last element is removed from the GUI. Initially I thought this was just a simple case of using the wrong index for deletion, but it turned out to be more than that.
This is what my ImageList component currently looks like:
import React from 'react';
import Dropzone from 'react-dropzone';
import cloneDeep from 'lodash.clonedeep';
import { ImageItem } from './image-item.js';
export class ImagesList extends React.Component {
constructor(props) {
super(props);
this.values = this.props.images || [];
this.onDrop = this.onDrop.bind(this);
this.addImages = this.addImages.bind(this);
this.deleteImage = this.deleteImage.bind(this);
this.imageChanged = this.imageChanged.bind(this);
}
onDrop(files) {
this.addImages(files);
}
onDropRejected() {
alert('Invalid file type');
}
addImages(files) {
files.forEach(file => {
this.values.push({
title: '',
description: '',
url: file.preview,
file,
});
});
this.forceUpdate();
}
deleteImage(index) {
console.log('index to delete', index);
console.log('images pre-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.values.splice(index, 1);
console.log('images post-delete', cloneDeep(this.values)); // deep-copy because logging is async
this.forceUpdate();
}
imageChanged(index, image) {
this.values[index] = image;
this.forceUpdate();
}
render() {
console.log('--------RENDER--------');
return (
<div className="image-list">
<div className="list-group">
{this.values.length === 0 ?
<div className="list-group-item">
No images
</div>
:
this.values.map((image, index) => {
console.log('rendering image', image);
return (
<ImageItem
key={index}
image={image}
onDelete={() => { this.deleteImage(index); }}
onChange={(item) => { this.imageChanged(index, item); }}
deletable={true}
/>
);
})
}
</div>
<Dropzone
multiple={true}
onDrop={this.onDrop}
onDropRejected={this.onDropRejected}
className="dropzone"
activeClassName="dropzone-accept"
rejectStyle={this.rejectStyle}
accept={'image/*'}
>
<span>Drop files here</span>
</Dropzone>
</div>
);
}
}
The ImagesList component can be initialized with some values (for the sake of debugging), which it uses during rendering. For example:
<ImagesList images={[
{ title: 'Image 1', description: 'Image 1 description', url: 'http://cssdeck.com/uploads/media/items/3/3yiC6Yq.jpg' },
{ title: 'Image 2', description: 'Image 2 description', url: 'http://cssdeck.com/uploads/media/items/4/40Ly3VB.jpg' },
{ title: 'Image 3', description: 'Image 3 description', url: 'http://cssdeck.com/uploads/media/items/0/00kih8g.jpg' },
]}/>
ImagesList renders an ImageItem component for each image. This is what this component looks like:
import React from 'react';
import { RIEInput, RIETextArea } from 'riek';
export class ImageItem extends React.Component {
constructor(props) {
super(props);
this.placeholder = {
title: 'Title',
description: 'Description',
};
this.value = this.props.image;
}
render() {
return (
<div className="list-group-item">
<div className="text-content">
<h4>
<RIEInput
className="description"
value={this.value.title.length <= 0 ?
this.placeholder.title : this.value.title}
change={(item) => {
this.value.title = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
/>
</h4>
<span>
<RIETextArea
className="description"
value={this.value.description.length <= 0 ?
this.placeholder.description : this.value.description}
change={(item) => {
this.value.description = item.value;
this.props.onChange(this.value);
}}
validate={(value) => value.length >= 1}
classEditing="form-control"
propName="value"
rows="2"
/>
</span>
</div>
<img className="thumb img-responsive"
style={{width: '20%' }}
src={this.value.url}
alt="Image"
data-action="zoom"
/>
{this.props.deletable ?
<div className="delete-btn">
<span onClick={this.props.onDelete}>
×
</span>
</div>
:
undefined }
</div>
);
}
}
Let's say I have three images, image A, B and C, and I want to delete image B. After pressing the delete button, image C will disappear from the GUI instead.
Inside the deleteImage() function of ImagesList, I am logging the index that is to be deleted and also log the values before and after the deletion. The index that is logged is correct, in this case that is index 1. Before the deletion the values are images A, B and C. After deletion the values are images A and C, as they should be.
I decided to do some logging inside the render() function of ImagesList as well. Unfortunately this also logs the correct values A and C, but A and B are actually rendered.
I have also tried to use the React state for this component instead of storing it in a local variable in conjunction with forceUpdate().
Another thing I have tried is to use the React Developer Tools plugin for Chrome. The Devtools also show the correct values, but the GUI still does not, as shown in this screenshot.
I'm currently out of ideas on what to try, any help would be appreciated!
Using the snippets I provided, you should be able to create a Meteor project and reproduce this bug.
With MasterAM's suggestion I managed to find two different solutions.
A.) Using componentWillUpdate()
The this.value variable is set only once namely in the constructor of the ImageItem component. To ensure that changes are properly delegated, you have to update this.value inside the componentWillUpdate() function. Something like:
componentWillUpdate(nextProps, nextState) {
this.value = nextProps.image;
}
B.) Using the property directly
This is definitely the more proper solution. Here we get rid of the local variable this.value inside the constructor of the ImageItem component.
Inside the render() function you replace this.value with this.props.image. Now without having to use the componentWillUpdate() function, everything works as expected.