Why do my Component lost while rendering the next page? - reactjs

I'm using MathJax for the first time and I did success to display some simple formule like
'a x b = c' in a quizz whith several questions.
On the first page, my formula displayed well but not the others, as you can see on the inspector the component disappear...
Here is the first question :
And the second question :
Here is the code where I am mounting the component :
function createRandomQuestions() {
var nbQuestions = 10;
var maxInt = 15;
var randomQuestions = [];
for (var i = 0; i < nbQuestions; i++) {
var randomInt1 = getRandomInt(maxInt);
var randomInt2 = getRandomInt(maxInt);
var enounceModel = "\\({" + randomInt1 + "}\\times{"+ randomInt2 + "} = \\)";
console.log(enounceModel)
var question = {
enounce: <MathJaxDisplay
enounceModel={enounceModel}/>
,
answer: randomInt1*randomInt2
}
randomQuestions.push(question);
}
CustomLogger("randomQuestions", randomQuestions, 'TableTestHome')
setQuestions(randomQuestions);
return randomQuestions;
}
And
MathJaxDisplay
is just a custom Component englobing the regex :
"\\({a}\\times{b} = \\)";
import React from 'react';
import { MathJax, MathJaxContext } from "better-react-mathjax";
const MathJaxDisplay = ({enounceModel}) => {
return (
<MathJaxContext>
<MathJax>
{enounceModel}
</MathJax>
</MathJaxContext>
);
}
export default MathJaxDisplay;
Thank you for your help

Related

Is it possible to show gradients with react-svgmt?

I would like to use react-svgmt to manipulate SVG's. These SVG's can be uploaded from the users. My problem is, that if the SVG has a gradient these parts of the SVG are not shown.
Has anybody a solution for my problem to reactivate these links after the import of the SVG via react-svgmt?
With the developertools of my browser I was be able the see that the elements are in the code, but the grandients will be linked to the elements and these links are broken.
I have seen that other people has the same problem with gradients and created an issue on github, but the developer did not respond.
I found a solution for my problem.
react-svgmt add to elements like linearGradients "-{nr}" to its id-names. Then this fill:url(#linear-gradient-2); does not work anymore in the style-tags, because it must be fill:url(#linear-gradient-2-{nr});.
So the first SVGProxy after loading is <SvgProxy selector="linearGradient" onElementSelected={handleGradients} ></ SvgProxy>
an the function handleGradients looks like following and saves the {nr}
const handleGradients = (gradients) => { if (typeof gradients.length !== 'undefined') { const output = gradients.map((g, i) => { nr = g.id.substring(g.id.lastIndexOf("-")+1); setNr(g.id.substring(g.id.lastIndexOf("-")+1)); if(g.href.baseVal.length > 0) { var base = g.href.baseVal; g.href.baseVal = base + '-' + nr; } return g; }); } else { nr = gradients.id.substring(gradients.id.lastIndexOf("-")+1); setNr(gradients.id.substring(gradients.id.lastIndexOf("-")+1)); if(gradients.href.baseVal.length > 0) { var base = gradients.href.baseVal; gradients.href.baseVal = base + '-' + nr; } } };
Then I had another SVGProxy
<SvgProxy selector="defs > style" onElementSelected={handleStyleElem} ></ SvgProxy>
with the following handlefunction which replaces all urls with the schema from above
const handleStyleElem = (svgStyle) => { var styleStr = svgStyle.outerHTML; styleStr = styleStr.replaceAll(/url\(#((\w|-)*)\);/g, 'url(#$1-'+ nr +');'); svgStyle.outerHTML = styleStr; };

React assign values to an array

I'm trying to handle some star rating according to some data from an api. The problem is that I have created an array for the stars and nothing is rendered. If I console .log inside the fetchMovieDetails I can see there is data. What am I missing? I even tried var stars = useState([]) but still same result
export default function MovieDetails({ match }) {
const [movie, setMovie] = useState({});
var stars = [];
useEffect(() => {
fetchMovieDetails();
}, []);
async function fetchMovieDetails() {
// get data from api
// Handle star rating
var rating = (response.rating/ 2).toFixed(1);
for (var i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
stars.push(star);
}
setMovie(response.data);
}
return (
<div className="movie-container">
<div className="movie-grid">
<div className="movie-rating">
<div className="star-rating">
// nothing is rendered here
{stars.map((star) => (
<span className="{`icon-star ${star}`}">
<span className="path1"></span>
<span className="path2"></span>
</span>
))}
{(movie.rating/ 2).toFixed(1)} / 5
</div>
</div>
</div>
</div>
);
}
I guest var stars is always re-declare. Why don't you using the useState like:
const [stars, setStarts] = useState([]);
useEffect(() => {
fetchMovieDetails();
}, []);
async function fetchMovieDetails() {
// get data from api
// Handle star rating
var rating = (response.rating/ 2).toFixed(1);
var stars = [];
for (var i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
stars.push(star);
}
setStarts(stars);
setMovie(response.data);
}
If you prefer to not to keep stars in the state, and if you need to move the star logic in to a separate section then useMemo will help with it.
async function fetchMovieDetails() {
// get data from api
.....
setMovie(response.data);
}
const stars = useMemo(() => {
if (!movie.rating) {
//if movie is not fetched then return an empty array
return [];
}
const rating = (movie.rating/ 2).toFixed(1);
const starsTemp = [];
for (let i = 0; i < 5; i++) {
var star = {};
if (i < ~~rating) star = 'filled';
else if (rating % 1 === 0) star = 'half-filled';
starsTemp.push(star);
}
return starsTemp;
}, [movie]); //when movie gets changed call this function
Why i prefer this is, lets say you can add a rating by clicking on a star, and then lets say you need to show the updated rating, with your approach you have to change two states, rating property of movie and the stars, so basically if a one state is depending on a another state, its better to do like this.

Is it possible to retrieve a component's instance from a React Fiber?

Before v16 of React -- that is, before the introduction of React fibers -- it was possible to take a DOM element and retrieve the React component instance as follows:
const getReactComponent = dom => {
let found = false;
const keys = Object.keys(dom);
keys.forEach(key => {
if (key.startsWith('__reactInternalInstance$')) {
const compInternals = dom[key]._currentElement;
const compWrapper = compInternals._owner;
const comp = compWrapper._instance;
found = comp;
}
});
return found || null;
};
This no longer works for React v16, which uses the new Fiber implementation. Specifically, the above code throws an error at the line const comparWrapper = compInternals._owner because there is no _owner property anymore. Thus you cannot also access the _instance.
My question here is how would we retrieve the instance from a DOM element in v16's Fiber implementation?
You may try the function below (updated to work for React <16 and 16+):
window.FindReact = function(dom) {
let key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
let internalInstance = dom[key];
if (internalInstance == null) return null;
if (internalInstance.return) { // react 16+
return internalInstance._debugOwner
? internalInstance._debugOwner.stateNode
: internalInstance.return.stateNode;
} else { // react <16
return internalInstance._currentElement._owner._instance;
}
}
Usage:
var someElement = document.getElementById("someElement");
FindReact(someElement).setState({test1: test2});
React 17 is slightly different:
function findReact(dom) {
let key = Object.keys(dom).find(key => key.startsWith("__reactFiber$"));
let internalInstance = dom[key];
if (internalInstance == null) return "internalInstance is null: " + key;
if (internalInstance.return) { // react 16+
return internalInstance._debugOwner
? internalInstance._debugOwner.stateNode
: internalInstance.return.stateNode;
} else { // react <16
return internalInstance._currentElement._owner._instance;
}
}
the domElement in this is the tr with the data-param-name of the field you are trying to change:
var domElement = ?.querySelectorAll('tr[data-param-name="<my field name>"]')

server handlers in Google app script sheets not working

I get this error when trying to execute this code from the script manager menu.
Error encountered: Script function not found: function click(e) {
var app = UiApp.getActiveApplication();
app.getElementById('label').setVisible(false);
SpreadsheetApp.getActiveSpreadsheet().show(ui);
return app;
}
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
Logger.log(row);
}
};
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "ShowUi",
functionName : "showSidebar"
}];
spreadsheet.addMenu("Script Center Menu", entries);
};
function click(e) {
var app = UiApp.getActiveApplication();
app.getElementById('label').setVisible(false);
SpreadsheetApp.getActiveSpreadsheet().show(ui);
return app;
}
function showSidebar(e){
var ui= UiApp.createApplication()
.setTitle('My UiApp Application')
.setWidth(250)
.setHeight(300);
var button = ui.createButton("I am a button!");
var handler = ui.createServerHandler(click);
button.addClickHandler(handler);
var label =ui.createLabel('The photograph on the dashboard taken years ago...').setId('label').setVisible(false);
handler.addCallbackElement(label).addCallbackElement(button);
ui.add(label);
ui.add(button);
SpreadsheetApp.getActiveSpreadsheet().show(ui);
return ui;
};
I'm sorry that I had to post here for probably obvious but I could not find the answer anywhere else on the internet. Any solution or alternate way i could do the same thing would be greatly appreciated. thanks!
Try something like this:
function showSidebar(e){
var ui= UiApp.createApplication()
.setTitle('My UiApp Application')
.setWidth(250)
.setHeight(300);
var label = ui.createLabel('The photograph on the dashboard taken years ago...').setId('label').setVisible(false);
ui.add(label);
ui.add(ui.createButton('I am a button!', ui.createServerHandler('onClick')).setId('button'));
SpreadsheetApp.getActiveSpreadsheet().show(ui);
return ui;
};
function onClick(e) {
var app = UiApp.getActiveApplication();
app.getElementById('label').setVisible(false);
//SpreadsheetApp.getActiveSpreadsheet().show(ui);
app.close();
return app;
};

Chain Reaction with Arrays and Distance

I want to make a program in which mines will appear on the screen after a user inputs the number of mines. Then, the user will click on one mine and set off a chain reaction that explodes the nearest two mines.
So far, my code can prompt the user for the number of mines, and then display them. The mines are buttons in which, when clicked, will be removed and an explosion will appear.
However, I am stuck with how I can handle the chain reaction. I am relatively new to coding in AS3 and therefore am stumped with no clue on how to approach this part of my program.
Code:
package
{
import flash.display.MovieClip;
import flash.text.TextField;
import flash.text.TextFieldType;
import flash.events.*;
public class Minefield extends MovieClip
{
var integer:int;
var iField:TextField = new TextField();
var button:iButton = new iButton();
var i:int;
var mines:Array = new Array();
public function Minefield()
{
var explosion:iExplosion = new iExplosion();
iField.type = "input";
iField.height = 18;
iField.x = 460;
iField.y = 275;
iField.border = true;
iField.restrict = "0-9";
iField.maxChars = 2;
stage.focus = iField;
addChild(iField);
addChild(button);
button.x = 450;
button.y = 175;
button.buttonMode = true;
button.addEventListener(MouseEvent.CLICK, UponClick);
}
function AddMines()
{
for (i = 0; i < integer; i++)
{
CreatorOfMine();
mines[i].addEventListener(MouseEvent.CLICK, UponMineClick)
mines[i].buttonMode = true;
}
}
function CreatorOfMine()
{
mines[i] = new Mine();
MineLocation()
}
function MineLocation()
{
mines[i].x = Math.round(Math.random() * 925);
mines[i].y = Math.round(Math.random() * 525);
mines[i].rotation = Math.random() * 360;
addChild(mines[i]);
}
function UponClick(e:MouseEvent)
{
integer = int(iField.text);
RemoverOfChildren();
}
function RemoverOfChildren()
{
removeChild(button);
removeChild(iField);
AddMines();
}
function UponMineClick(event:MouseEvent){
var mineObject:Mine = Mine(event.currentTarget)
var expl:iExplosion = new iExplosion()
expl.x = mineObject.x
expl.y = mineObject.y
expl.rotation = mineObject.rotation
addChild(expl)
removeChild(mineObject)
}
}
}
}
Information you may need/want:
Stage size is 1024 x 600 (px)
Size of mine(s) is 40 x 40 (px)
Size of explosion is 40 x 40 (px)
Looks like a good case for recursion. Pseudo-code:
Function ExplodeMine(mine) {
mine.boooooooooom()
nearest = findNearestUnexplodedMines()
foreach(nextMine in nearest) {
ExplodMine(nextMine);
}
}
Just start ExplodeMine on the first mine that is clicked.

Resources