why dropdown in tables cell closes when the dropdowns page changes (kendoreact) - combobox

i have a grid with kendoreact that has a dropdown in its cell. i add a pager to the dropdown. but when i click on the pages, the dropdown close.
i have the same combobox in the form and it works good.
i tried a lot and worked on the pager and combobox props. but it was useless. perhaps its a behaviour from the grid. please help
import React, { useRef } from "react";
import { ComboBox } from "#progress/kendo-react-dropdowns";
import { filterBy } from "#progress/kendo-data-query";
import { Pager } from "#progress/kendo-react-data-tools";
import {
IntlProvider,
load,
LocalizationProvider,
loadMessages,
} from "#progress/kendo-react-intl";
import faMessages from "../../../../common/fa.json";
import persianJs from "persianjs";
// import Combobox from "../../../RPKCustomComponent/Combobox";
loadMessages(faMessages, "fa-IR");
const selectField = "selected";
const expandField = "expanded";
const dataItemKey = "value";
const textField = "label";
const subItemsField = "items";
const fields = {
selectField,
expandField,
dataItemKey,
subItemsField,
};
const initialType = "numeric";
const initialPageState = {
skip: 0,
take: 10,
total: 0,
buttonCount: 5,
type: initialType,
info: true,
// pageSizes: true,
previousNext: true,
responsive: true,
};
export default class ComboBoxCell extends React.Component {
state = {
data: [],
pageState: initialPageState,
take: 10,
skip: 0,
mammad: false,
};
handlepageChange = async (event) => {
// event.syntheticEvent.isDefaultPrevented();
// await this.setState({ skip: event.skip, take: event.take, mammad: true });
await this.props.getCombo({
id: this.props.dataItem.Id,
pageIndex: event.skip/10,
pageSize: 10,
search: "",
});
this.setState({mammad:true})
};
handleChange = (e) => {
this.props.onChange({
dataItem: this.props.dataItem,
field: this.props.field,
syntheticEvent: e.syntheticEvent,
value: e.target.value,
});
this.setState({mammad:true})
};
handleOnClick = async (e) => {
setTimeout(async () => {
if (!this.props.data && this.props.getCombo) {
await this.props.getCombo(true, this.state.skip / 10, 10, "");
}
}, 0);
this.setState({mammad:true})
};
componentDidMount() {
let dropdowntree = document.getElementById(`${this.props.id}`);
if (dropdowntree) {
dropdowntree.setAttribute("name", this.props.field);
}
this.textInput = React.createRef();
if (this.props.data?.List) {
let badData = this.props.data?.List.find((item) => {
if (item.value < 0 || item.label == null || item.label == undefined) {
return item;
}
});
if (badData) {
this.props.data.List.map((item) => {
if (item.label == null || item.label == undefined) {
item.label = "";
}
});
console.log("دیتای کمبو از سمت بک مشکل داره");
}
}
if (this.props.data?.List) {
let badData = this.props.data.List.find((item) => {
if (item.value < 0 || item.label == null || item.label == undefined) {
return item;
}
});
if (badData) {
this.props.data.List.map((item) => {
if (
item.label == null ||
item.label == undefined ||
item.label == false
) {
item.label = " ";
}
});
console.log("دیتای کمبو از سمت بک مشکل داره");
}
this.props.data.List.length > 0 &&
this.props.data.List.map(
(item) =>
(item.label =
item.label.length > 0 &&
persianJs(item.label)?.arabicChar()?._str)
);
}
this.setState({
data: this.props.data?.List,
pageState: {
...this.state.pageState,
total: this.props.data?.RecordCount ?? 0,
},
});
}
filterChange = (event) => {
this.setState({
data: this.filterData(event.filter),
});
};
filterData(filter) {
if (this.props.data?.List) {
const data = this.props.data?.List?.slice();
return filterBy(data, filter);
}
}
render() {
// let test=document.getElementsByClassName("comboFooterPageNumber")[0]
// console.log(test);
// document.getElementsByClassName("comboFooterPageNumber")[0]
// .addEventListener("click",(e)=>{
// console.log(e);
// });
// document
// .getElementsByClassName(this.props?.realurl)[0]
// .addEventListener("dblclick", this.AddInCell);
// console.log(this.state.skip);
console.log(this.state.mammad);
const { dataItem, field } = this.props;
const dataValue = dataItem[field] === null ? "" : dataItem[field];
const dataLableValue =
dataItem[field] === null ? "" : dataItem[field]?.label;
let dropValue = this.props.data?.List
? this.props.data?.List.find((c) => c.value == dataValue)
: null;
let dropLabel = this.props.data?.List
? this.props.data?.List.find((c) => c.value == dataValue)?.label
: null;
const listNoDataRender = (element) => {
const noData = (
<h4
style={{
fontSize: "1em",
}}
>
<span
className="k-icon k-i-warning"
style={{
fontSize: "1.5em",
}}
/>
<br />
<br />
موردی یافت نشد!
</h4>
);
return React.cloneElement(element, { ...element.props }, noData);
};
return (
<td className="cell-input">
{dataItem.inEdit && this.props.editable ? (
<ComboBox
{...this.props}
data={this.state.data ?? []}
onChange={this.handleChange}
textField={textField}
dataItemKey={dataItemKey}
filterable={true}
opened={this.state.mammad}
closed={!this.state.mammad}
onFilterChange={this.filterChange}
required={this.props.required}
onOpen={this.handleOnClick}
value={dropValue ?? dataValue}
listNoDataRender={listNoDataRender}
ariaLabelledBy={this.props.ariaLabelledBy ?? ""}
name={this.props.field}
footer={
// (!this.props.clientSide || this.props.allowNewForm) && (
<div>
{/* {this.props.allowNewForm && (
<>
<span className="comboFooter"></span>
<p onClick={others.setshowmodal}>افزودن گزینه جدید</p>
</>
)} */}
{/* {!this.props.clientSide && ( */}
<div className="comboFooterPageNumber">
<LocalizationProvider language="fa">
<Pager
skip={this.state.skip}
take={this.state.take}
total={this.state.pageState.total}
buttonCount={this.state.pageState.buttonCount}
info={this.state.pageState.info}
type={this.state.pageState.type}
previousNext={this.state.pageState.previousNext}
onPageChange={this.handlepageChange}
ref={this.textInput}
/>
</LocalizationProvider>
</div>
{/* )} */}
</div>
// )
}
/>
) : (
dropLabel ?? dataLableValue
)}
</td>
);
}
}

Related

Why from new component my old component useEffect calls are going

I am having a home component in which i conditionally show another component and on both the component i have setInterval for repetative api calls but when i move to 2nd component then my 1st component useEffect doesn't stop it continuously make api calls, here is the code
HomePage
import React, { useEffect, useState } from "react";
import '../../App.css';
import DetailsPage from '../DetailPage/index';
import Login from "../Login/index";
import LoginPage from '../Login/index';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
function HomePage(props){
const [homeData, setHomeData] = useState([]);
const [allTickerData, setAllTickerData] = useState(null);
const [getAllTickerDataGraph, setGetAllTickerDataGraph] = useState(null);
const [showDetails, setShowDetails] = useState(false);
const [individualDetails, setIndividualDetails] = useState(null);
const [userToken, setUserToken] = useState(false);
const [userTokenValue, setUserTokenValue] = useState('');
const [selectedTime,setSelectedTime] = useState(1);
const [ticker, selectedTicker] = useState(null);
const options = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '',
},
},
};
const labels = getAllTickerDataGraph && Object.keys(getAllTickerDataGraph);
const values = getAllTickerDataGraph && Object.values(getAllTickerDataGraph);
const data = {
labels,
datasets: [
{
label: 'PNL',
data: labels && labels.map((item, index) => values && values[index]),
borderColor: 'rgba(99, 99, 132, 0.5)',
backgroundColor: 'rgba(99, 99, 132, 0.5)',
},
]
}
useEffect(() => {
let token = localStorage.getItem('userToken');
if(token && token.length > 0){
setUserToken(true);
setUserTokenValue(token);
}
if(token != null){
getData();
getAllTicker();
getAllTickerData(selectedTime);
let intervalId = setInterval(function(){
getData();
getAllTickerData(selectedTime);
getAllTicker();
}, 20000);
return(() => {
clearInterval(intervalId)
})
}
},[userTokenValue,userToken, selectedTime])
function getData() {
var t = `Bearer ${userTokenValue}`
t = t.replace(/"/g, "")
const requestOptions = {
method: 'POST',
};
if(t && t.length > 12){
fetch(`${process.env.REACT_APP_PROXY}/get-all-tickers`, requestOptions).then((data) => {
return data.json()
}).then((function(data){
let arrayData = data && Object.entries(data);
setHomeData(arrayData);
}))
}
}
function getAllTicker() {
var t = `Bearer ${userTokenValue}`
t = t.replace(/"/g, "")
const requestOptions = {
method: 'POST',
};
if(t && t.length > 12 ){
fetch(`${process.env.REACT_APP_PROXY}/get-all`, requestOptions).then((data) => {
return data.json()
}).then((function(data){
let arrayData = data && Object.entries(data)
setAllTickerData(arrayData);
}))
}
}
function getAllTickerData(days){
var d = new Date;
var currentDate = [ d.getFullYear(),
d.getMonth()+1,
d.getDate()].join('-')+' '+
[d.getHours(),
d.getMinutes(),
d.getSeconds()].join(':');
let lastWeek = new Date(d.getFullYear(), d.getMonth(), d.getDate()-days);
lastWeek = [ lastWeek.getFullYear(),
lastWeek.getMonth()+1,
lastWeek.getDate()].join('-')+' '+
[lastWeek.getHours(),
lastWeek.getMinutes(),
lastWeek.getSeconds()].join(':');
let body = {
ticker: '',
from_date: lastWeek,
to_date: currentDate
}
var t = `Bearer ${userTokenValue}`
t = t.replace(/"/g, "")
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': t },
body: JSON.stringify(body)
};
if(t && t.length > 12 && !showDetails){
fetch(`${process.env.REACT_APP_PROXY}/get-cumilative_pnl`, requestOptions).then((data) => {
return data.json()
}).then((function(data){
setGetAllTickerDataGraph(data);
}))
}
}
function getTickerData(ticker) {
selectedTicker(ticker)
var d = new Date;
var currentDate = [ d.getFullYear(),
d.getMonth()+1,
d.getDate()].join('-')+' '+
[d.getHours(),
d.getMinutes(),
d.getSeconds()].join(':');
let lastWeek = new Date(d.getFullYear(), d.getMonth(), d.getDate()-1);
lastWeek = [ lastWeek.getFullYear(),
lastWeek.getMonth()+1,
lastWeek.getDate()].join('-')+' '+
[lastWeek.getHours(),
lastWeek.getMinutes(),
lastWeek.getSeconds()].join(':');
let body = {
ticker: `${ticker}`,
from_date: lastWeek,
to_date: currentDate
}
var t = `Bearer ${userTokenValue}`
t = t.replace(/"/g, "")
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': t },
body: JSON.stringify(body)
};
if(t && t.length > 12){
fetch(`${process.env.REACT_APP_PROXY}/get-data`, requestOptions).then((data) => {
return data.json()
}).then((function(data){
let arrayData = data && Object.entries(data);
console.log(arrayData,"array ka data ")
setShowDetails(true);
setIndividualDetails(arrayData);
}))
}
}
const handleDropdownChange = (e) => {
setSelectedTime(e.target.value);
getAllTickerData(e.target.value);
}
return(
<div>
{
userToken && userToken && userTokenValue.length > 0 && userTokenValue != null?
<div>
{
showDetails && showDetails ? <DetailsPage ticker={ticker} data={individualDetails} allTickerData={allTickerData}/> :
<div>
<div className="top-div">
<div className="individual-div">PNL: {allTickerData && allTickerData[0] && allTickerData[0][1] && allTickerData[0][1].pnl && Math.round(allTickerData[0][1].pnl)} </div>
<div className="individual-div">POSITION: {allTickerData && allTickerData[0] && allTickerData[0][1] && allTickerData[0][1].position && Math.round(allTickerData[0][1].position)} </div>
</div>
<div className="low-div">
<div className="left-div">
<table className="table">
<tr>
<th>Ticker</th>
<th>PNL</th>
<th>Position</th>
</tr>
</table>
<table className="table">
{
homeData && Object.keys(homeData).length > 0 ?
homeData.map((data) => {
return(
<tr key={data[0]} onClick={() => getTickerData(data[0])}>
<td>{data[0]}</td>
<td>{data[1].pnl && data[1].pnl.toFixed(4)}</td>
<td>{data[1].position && data[1].position.toFixed(4)}</td>
</tr>
)
}) : <div>Fetching Data... </div>
}
</table>
</div>
<div className="right-div">
<Line options={options} data={data} />
<div className="buttons-report-home">
{/* {/* <div onClick={() => getAllTickerData(1)} className="single-button">1 D</div> */}
<select name="days" id="days" onChange={(e) => handleDropdownChange(e)}>
<option value="1">1 D</option>
<option value="7">1 W</option>
<option value="30">1 M</option>
<option value="180">6 M</option>
</select>
</div>
</div>
</div>
</div>
}
</div> : <LoginPage />
}
</div>
)
}
export default HomePage;
my another component Details page
import React, { useEffect, useState } from "react";
import '../../App.css';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from 'chart.js';
import { Line } from 'react-chartjs-2';
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
function DetailPage(props){
const [individualDetails, setIndividualDetails] = useState(props.data);
const [userToken, setUserToken] = useState(false);
const [userTokenValue, setUserTokenValue] = useState(null);
const [tick, setTick] = useState(props && props.data && props.data[0] && props.data[0]);
const [all, setAll] = useState(null);
const [timeSelected, setTimeSelected] = useState(1);
const options = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '',
},
},
};
const options1 = {
responsive: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: '',
},
},
};
const labels = (individualDetails && individualDetails[0] && individualDetails[0][1] && Object.keys(individualDetails[0][1])) || (props && props.data && props.data[0] && props.data[0][1] && Object.keys(props.data[0][1]));
const values = (individualDetails && individualDetails[0] && Object.values(individualDetails[0][1])) || (props && props.data && props.data[0] && props.data[0][1] && Object.values(props.data[0][1]));
const data = {
labels,
datasets: [
{
label: 'PNL',
data: labels && labels.map((item, index) => values && values[index] && values[index].pnl),
borderColor: 'rgba(99, 99, 132, 0.5)',
backgroundColor: 'rgba(99, 99, 132, 0.5)',
},
],
};
const data1 = {
labels,
datasets: [
{
label: 'POSITION',
data: labels && labels.map((item, index) => values && values[index] && values[index].position),
borderColor: 'rgba(255, 99, 132, 0.5)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
}
],
};
useEffect(() => {
let token = localStorage.getItem('userToken');
if(token && token.length > 0){
setUserToken(true);
setUserTokenValue(token);
}
var tick = props && props.ticker;
setAll(props.allTickerData);
setTick(tick);
let intervalId =
setInterval(function(){
getTickerData(tick, timeSelected)
}, 20000);
return(() => {
clearInterval(intervalId)
})
},[individualDetails, tick,timeSelected])
function getTickerData(ticker, noOfDays) {
console.log(ticker, noOfDays,"hey ya ya aya ay")
var d = new Date;
var currentDate = [ d.getFullYear(),
d.getMonth()+1,
d.getDate()].join('-')+' '+
[d.getHours(),
d.getMinutes(),
d.getSeconds()].join(':');
let lastWeek = new Date(d.getFullYear(), d.getMonth(), d.getDate() - noOfDays);
lastWeek = [ lastWeek.getFullYear(),
lastWeek.getMonth()+1,
lastWeek.getDate()].join('-')+' '+
[lastWeek.getHours(),
lastWeek.getMinutes(),
lastWeek.getSeconds()].join(':');
let body = {
ticker: `${ticker}`,
from_date: lastWeek,
to_date: currentDate
}
var t = `Bearer ${userTokenValue}`
t = t.replace(/"/g, "")
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': t },
body: JSON.stringify(body)
};
if(t && t.length > 12){
fetch(`${process.env.REACT_APP_PROXY}/get-data`, requestOptions).then((data) => {
return data.json()
}).then((function(data){
let arrayData = data && Object.entries(data);
setIndividualDetails(arrayData);
}))
}
}
const handleDropdownChange = (e) => {
getTickerData(props && props.data && props.data[0] && props.data[0][0], e.target.value);
setTimeSelected(e.target.value);
}
const refreshPage = () => {
window.location.reload();
}
return(
<div className="detail-div">
<div className="top-section">
<div className="back-button" onClick={() => refreshPage()}><span className="back-text">Back</span></div>
<div className="top-div">
<div className="individual-div">PNL: {all && all[0] && all[0][1] && all[0][1].pnl && all[0][1].pnl} </div>
<div className="individual-div">POSITION: {all && all[0] && all[0][1] && all[0][1].position && all[0][1].position} </div>
</div>
</div>
<div className="ticker-name">{tick}</div>
<div className="graph-parent">
<div className="graph-div">
<Line options={options} data={data} />
<Line options={options1} data={data1} />
</div>
<div className="buttons-report">
<select name="days" id="days" onChange={(e) => handleDropdownChange(e)}>
<option value="1">1 D</option>
<option value="7">1 W</option>
<option value="30">1 M</option>
<option value="180">6 M</option>
</select>
{/* {/* <div onClick={() => getTickerData(props && props.data && props.data[0] && props.data[0][0], 1)} className="single-button">1 D</div> */}
</div>
</div>
</div>
)
}
export default DetailPage;
i just want when Detials component render i don't want home page setInterval to work, i am pretty new to hooks, thanks in advance.
The HomePage component is still mounted and being rendered when conditionally rendering the DetailPage component, so the interval timer it instantiated is still running and never cleaned up.
A solution is to add showDetails state to the useEffect hook's dependency and clear the interval when showDetails is true, or rather, only start the interval when showDetails is false.
Example:
useEffect(() => {
const token = localStorage.getItem('userToken');
if (token?.length) {
setUserToken(true);
setUserTokenValue(token);
}
const getAllData = () => {
getData();
getAllTicker();
getAllTickerData(selectedTime);
};
getAllData();
if (token !== null && !showDetails) {
const intervalId = setInterval(getAllData, 20000);
return () => {
clearInterval(intervalId);
}
}
}, [userTokenValue, userToken, selectedTime, showDetails]);

How to change the background color

I am new to React and It really interesting to work on it. As per my current application which is a Quiz app, If user selects any of the options it should change the background color to red or green. I am using the semantic UI and to the menu item, I am passing the data with an on-click event where I am storing the selected answer from the user. But I am unable to change the color wrt the correct answer or wrong answer. Here is the code.
import React, { Component } from 'react';
import {
Container,
Segment,
Item,
Divider,
Button,
Icon,
Message,
Menu,
Header
} from 'semantic-ui-react';
import Swal from 'sweetalert2';
import Loader from '../Loader';
import Countdown from '../Countdown';
import Result from '../Result';
import Offline from '../Offline';
import he from 'he';
import { getRandomNumber } from '../../utils/getRandomNumber';
class Quiz extends Component {
constructor(props) {
super(props);
this.state = {
quizData: null,
isLoading: true,
questionIndex: 0,
correctAnswers: 0,
userSlectedAns: null,
quizIsCompleted: false,
questionsAndAnswers: [],
isOffline: false,
bgColor: ""
};
this.timeTakesToComplete = undefined;
this.setData = this.setData.bind(this);
this.handleItemClick = this.handleItemClick.bind(this);
this.handleNext = this.handleNext.bind(this);
this.timesUp = this.timesUp.bind(this);
this.timeAmount = this.timeAmount.bind(this);
this.renderResult = this.renderResult.bind(this);
this.retakeQuiz = this.retakeQuiz.bind(this);
this.startNewQuiz = this.startNewQuiz.bind(this);
this.resolveError = this.resolveError.bind(this);
}
componentDidMount() {
const { API } = this.props;
fetch(API)
.then(respone => respone.json())
.then(result => setTimeout(() => this.setData(result.results), 1000))
.catch(error => setTimeout(() => this.resolveError(error), 1000));
}
resolveError(error) {
if (!navigator.onLine) {
this.setState({ isOffline: true });
console.log('Connection problem');
} else {
this.setState({ isOffline: true });
console.log('API problem ==> ', error);
}
}
setData(results) {
if (results.length === 0) {
const message =
"The API doesn't have enough questions for your query<br />" +
'(ex. Asking for 50 questions in a category that only has 20).' +
'<br /><br />Please change number of questions, difficulty level ' +
'or type of questions.';
return Swal.fire({
title: 'Oops...',
html: message,
type: 'error',
timer: 10000,
onClose: () => {
this.props.backToHome();
}
});
}
const quizData = results;
const { questionIndex } = this.state;
const outPut = getRandomNumber(0, 3);
const options = [...quizData[questionIndex].incorrect_answers];
options.splice(outPut, 0, quizData[questionIndex].correct_answer);
this.setState({ quizData, isLoading: false, options, outPut });
}
handleItemClick(e, { name }) {
const {
userSlectedAns,
quizData,
questionIndex,
} = this.state;
this.setState({ userSlectedAns: name });
console.log(name);
if (userSlectedAns === he.decode(quizData[questionIndex].correct_answer)) {
this.state.active = 'green';
}
}
handleNext() {
const {
userSlectedAns,
quizData,
questionIndex,
correctAnswers,
questionsAndAnswers
} = this.state;
let point = 0;
if (userSlectedAns === he.decode(quizData[questionIndex].correct_answer)) {
point = 1;
}
questionsAndAnswers.push({
question: he.decode(quizData[questionIndex].question),
user_answer: userSlectedAns,
correct_answer: he.decode(quizData[questionIndex].correct_answer),
point
});
if (questionIndex === quizData.length - 1) {
this.setState({
correctAnswers: correctAnswers + point,
userSlectedAns: null,
isLoading: true,
quizIsCompleted: true,
questionIndex: 0,
options: null,
questionsAndAnswers
});
return;
}
const outPut = getRandomNumber(0, 3);
const options = [...quizData[questionIndex + 1].incorrect_answers];
options.splice(outPut, 0, quizData[questionIndex + 1].correct_answer);
this.setState({
correctAnswers: correctAnswers + point,
questionIndex: questionIndex + 1,
userSlectedAns: null,
options,
outPut,
questionsAndAnswers
});
}
timesUp() {
this.setState({
userSlectedAns: null,
isLoading: true,
quizIsCompleted: true,
questionIndex: 0,
options: null
});
}
timeAmount(timerTime, totalTime) {
this.timeTakesToComplete = {
timerTime,
totalTime
};
}
renderResult() {
setTimeout(() => {
const { quizData, correctAnswers, questionsAndAnswers } = this.state;
const { backToHome } = this.props;
const resultRef = (
<Result
totalQuestions={quizData.length}
correctAnswers={correctAnswers}
timeTakesToComplete={this.timeTakesToComplete}
questionsAndAnswers={questionsAndAnswers}
retakeQuiz={this.retakeQuiz}
backToHome={backToHome}
/>
);
this.setState({ resultRef, questionsAndAnswers: [] });
}, 2000);
}
retakeQuiz() {
const { quizData, questionIndex } = this.state;
const outPut = getRandomNumber(0, 3);
const options = [...quizData[questionIndex].incorrect_answers];
options.splice(outPut, 0, quizData[questionIndex].correct_answer);
this.setState({
correctAnswers: 0,
quizIsCompleted: false,
startNewQuiz: true,
options,
outPut
});
}
startNewQuiz() {
setTimeout(() => {
this.setState({ isLoading: false, startNewQuiz: false, resultRef: null });
}, 1000);
}
render() {
const {
quizData,
questionIndex,
options,
userSlectedAns,
isLoading,
quizIsCompleted,
resultRef,
startNewQuiz,
isOffline
// outPut,
// correctAnswers,
} = this.state;
// console.log(userSlectedAns);
// console.log(questionIndex, outPut);
// console.log('Score ==>', correctAnswers);
if (quizIsCompleted && !resultRef) {
this.renderResult();
// console.log('Redirecting to result');
}
if (startNewQuiz) {
this.startNewQuiz();
}
return (
<Item.Header>
{!isOffline && !quizIsCompleted && isLoading && <Loader />}
{!isOffline && !isLoading && (
<Container>
<Segment>
<Item.Group divided>
<Item>
<Item.Content>
<Item.Extra>
<Header as="h1" block floated="left">
<Icon name="info circle" />
<Header.Content>
{`Question No.${questionIndex + 1} of ${
quizData.length
}`}
</Header.Content>
</Header>
<Countdown
countdownTime={this.props.countdownTime}
timesUp={this.timesUp}
timeAmount={this.timeAmount}
/>
</Item.Extra>
<br />
<Item.Meta>
<Message size="huge" floating>
<b>{`Q. ${he.decode(
quizData[questionIndex].question
)}`}</b>
</Message>
<br />
<Item.Description>
<h3>Please choose one of the following answers:</h3>
</Item.Description>
<Divider />
<Menu vertical fluid size="massive">
{options.map((option, i) => {
let letter;
switch (i) {
case 0:
letter = 'A.';
break;
case 1:
letter = 'B.';
break;
case 2:
letter = 'C.';
break;
case 3:
letter = 'D.';
break;
default:
letter = i;
break;
}
const decodedOption = he.decode(option);
return (
<Menu.Item
key={decodedOption}
name={decodedOption}
active={userSlectedAns === decodedOption}
onClick={this.handleItemClick}
// style={{backgroundColor: this.state.bgColor }}
>
<b style={{ marginRight: '8px' }}>{letter}</b>
{decodedOption}
</Menu.Item>
);
})}
</Menu>
</Item.Meta>
<Divider />
<Item.Extra>
<Button
primary
content="Next"
onClick={this.handleNext}
floated="right"
size="big"
icon="right chevron"
labelPosition="right"
disabled={!userSlectedAns}
/>
</Item.Extra>
</Item.Content>
</Item>
</Item.Group>
</Segment>
<br />
</Container>
)}
{quizIsCompleted && !resultRef && (
<Loader text="Getting your result." />
)}
{quizIsCompleted && resultRef}
{isOffline && <Offline />}
</Item.Header>
);
}
}
export default Quiz;
I checked your handleItemClick function and found that you're mutating the state directly. You should use setState() to update the state, that's the basic thing about React's state mutation.
Please change the following:
if (userSlectedAns === he.decode(quizData[questionIndex].correct_answer)) {
this.state.active = 'green';
}
To this way:
if (userSlectedAns === he.decode(quizData[questionIndex].correct_answer)) {
this.setState({
active: 'green'
});
}

Cannot read property clientHeight document in jest test cases

I am very new to write test cases, i tried to write test cases for my dashboard page and i am getting Cannot read property 'clientHeight' of undefined error, please see my all dashboard.js page below & my test cases file. please help me on this
getting Error in this line:
var t = window.innerHeight - document.getElementsByClassName('nep-header')[0].clientHeight - 20 + "p
My Dashboard.js page:
import { CompanyDashboard, DeleteCompanyDashboard } from "../../APICall/CompanyDashboard";
import React from "react";import { GetimageUrl } from "../../Common/UtilityFunctions";
import { Table, Modal, Button, Message } from "#maknowledgeservices/neptune";
import './Dashboard.css';
import { dateFormatConversionForSorting } from '../../Common/UtilityFunctions';
import { setTimeout } from "timers";
import Loader from '../../Components/Loader/Loader';
var successMessage = false;
var errorMessage = false;
var showing = true;
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.showing = true;
setTimeout(() => {
var divsToHide = document.getElementsByClassName("nep-table-empty"); //divsToHide is an array
for (var i = 0; i < divsToHide.length; i++) {
divsToHide[i].style.visibility = "hidden"; // or
divsToHide[i].style.display = "none"; // depending on what you're doing
}
}, 10);
this.state = {
visible: false,
DeleteCompStr: "",
DeleteCompID: 0,
columnDefs: [
{
title: "Company", render: "Companyname", fixed: "left", width: 320, sorter: order => (a, b) => {
if (order === 'asc') return a.Companyname.localeCompare(b.Companyname)
return b.Companyname.localeCompare(a.Companyname)
}
},
{
title: 'Year End', width: 95, render: d => this.renderMethod(d.financeYearEndMonth + " " + d.financeYearEnd), sorter: order => (a, b) => {
if (order === 'asc') return dateFormatConversionForSorting(a.financeYearEndMonth + " " + a.financeYearEnd).localeCompare(dateFormatConversionForSorting(b.financeYearEndMonth + " " + b.financeYearEnd))
return dateFormatConversionForSorting(b.financeYearEndMonth + " " + b.financeYearEnd).localeCompare(dateFormatConversionForSorting(a.financeYearEndMonth + " " + a.financeYearEnd))
}
},
{
title: 'LTM Financials', width: 95, render: d => this.renderMethod(d.ltmMonth + " " + d.ltmYear), sorter: order => (a, b) => {
if (order === 'asc') return dateFormatConversionForSorting(a.ltmMonth + " " + a.ltmYear).localeCompare(dateFormatConversionForSorting(b.ltmMonth + " " + b.ltmYear))
return dateFormatConversionForSorting(b.ltmMonth + " " + b.ltmYear).localeCompare(dateFormatConversionForSorting(a.ltmMonth + " " + a.ltmYear))
}
},
{
title: "AccuRate", width: 95, render: s => this.StatusIndicator(s.accurate),
},
{
title: "Financial Trends", width: 95, render: s => this.StatusIndicator(s.finTrend),
},
{
title: "Newsflow Trends", width: 115,
render: (s) => (
<div style={{ cursor: "default" }}>
{this.StatusIndicator(s.newsflow)}
<a className="tooltip" style={{ float: "right" }}
onClick={this.show.bind(this, s)}
><i style={{ cursor: "pointer" }} className="icon-Delete" name="delete"
onMouseOver={({ target }) => target.style.color = '#021155'}
onMouseOut={({ target }) => target.style.color = '#75787B'} /></a>
</div>
),
}
],
companyCount: '',
rowData: [],
res: ""
}
this.show = this.show.bind(this)
}
show(selRowVal) {
this.setState({
visible: true,
})
this.DeleteCompID = selRowVal.id;
this.DeleteCompStr = selRowVal.Companyname;
}
handleOk = () => {
DeleteCompanyDashboard("DeleteCompany/" + this.DeleteCompID).then(responce => {
if (responce.data === true) {
if (successMessage === false) {
successMessage = true;
Message.success(this.DeleteCompStr + ' Company has been deleted successfully', 7, {
onClose: () => {
successMessage = false;
}
});
}
}
else {
if (errorMessage === false) {
errorMessage = true;
Message.error('Server error', 7, {
onClose: () => {
errorMessage = false;
}
});
}
}
this.componentDidMount();
this.setState({
visible: false,
});
});
}
handleCancel = () => {
this.setState({
visible: false,
})
}
renderMethod(params) {
return params.substring(3);
}
handleRemove(selectedValue) {
this.show();
}
StatusIndicator(params) {
switch (params) {
case 'Positive':
return <span style={{ color: '#388E3C' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Negative':
return <span style={{ color: '#C62828' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Stable':
return <span style={{ color: '#C68700' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
case 'Neutral':
return <span style={{ color: '#C68700' }}><img className="indicaterGap" src={GetimageUrl(params)}></img>{params}</span>
break;
default:
return <span style={{ color: '#55565A' }}>{params}</span>
break;
}
}
componentDidMount() {
CompanyDashboard("GetCompany").then(responce => {
this.showing = false;
this.setState({
rowData: responce.data,
companyCount: responce.data.length === 0 || undefined ? 0 : responce.data.length
});
});
this.tableHeight();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.companyCount !== this.state.companyCount) {
CompanyDashboard("GetCompany").then(responce => {
this.setState({ rowData: responce.data, companyCount: responce.data.length === 0 || undefined ? 0 : responce.data.length });
});
}
this.tableHeight();
}
tableHeight() {
var t = window.innerHeight - document.getElementsByClassName('nep-header')[0].clientHeight - 20 + "px"
var ele = document.getElementById("pr");
ele.style.height = t.toString();
}
rowClicked(e) {
setTimeout(() => {
let selectedCompany = this.state.rowData.filter(x => x.cik == e.cik);
selectedCompany = selectedCompany.map(function (obj) {
let val = obj.Companyname;
delete obj['Companyname']
obj.companyname = val;
return obj
}
);
sessionStorage.setItem("selectedCompany", JSON.stringify(selectedCompany));
const { from } = {
from: { pathname: "/company" }
};
this.props.history.push(from);
}, 300)
}
handleClick = (value = this.state.value) => {
value += Math.random() * 12
if (value >= 100) {
value = 100
this.setState({ value })
} else {
this.setState({ value }, () => {
setTimeout(this.handleClick, 320)
})
}
}
render() {
return (
<div id="pr" className="tableSpace">
<label>Companies <span className="tableSpan">({this.state.companyCount})</span></label>
<div style={{ display: (this.showing ? 'block' : 'none') }} >
<Loader />
</div>
<Table
keygen="cik"
striped
bordered
fixed="both"
className="tableClass"
bordered fixed="both"
width={1024}
columns={this.state.columnDefs}
data={this.state.rowData}
onRowClick={this.rowClicked.bind(this)}
/>
<Modal
visible={this.state.visible}
width={500}
title=" Delete Company"
onClose={this.handleCancel}
maskCloseAble={false}
footer={[
<Button key="cancel" className="nep-button nep-button-secondary" onClick={this.handleCancel}>
Cancel
</Button>,
<Button key="ok" type="primary" onClick={this.handleOk}>
Delete
</Button>,
]} >
The company <b>{this.DeleteCompStr}</b> will be permanently deleted and this action cannot be undone. <br /> Are you sure you want to proceed ?
</Modal>
</div>
);}}export default Dashboard;`
My dashbord test cases file code:
import React from 'react'
import { render, cleanup } from '#testing-library/react';
import Dashboard from "../Components/Dashboard/Dashboard"
import axios from "axios";
import { shallow } from 'enzyme'
import { Table } from "#maknowledgeservices/neptune";
afterEach(cleanup);
jest.mock('axios');
it("should render initial layout", () => {
const component = shallow(<Dashboard />);
expect(component.getElements()).toMatchSnapshot();
});
it("test to render Dashboard component", async () => {
axios.post.mockResolvedValue({ data: [{ Companyname: "xyz", accurate: "Positive", }], status: 200, statusText: "OK" });
await render(<Dashboard />, Table);
var result = render(<Dashboard />);
expect(result).toBeTruthy();
});

How to render only 5 items in react autosuggest?

I'am using react autosuggest npm package to get the json data and display it. I want to display only 5 items. How to do it?
Form.js
import React from 'react'
import Autosuggest from 'react-autosuggest';
import cities from 'cities.json';
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
);
};
const getSuggestionValue = suggestion => suggestion.name;
const renderSuggestion = suggestion => (
<div>
{console.log('suggestion', suggestion)}
{suggestion.name}
</div>
);
class Form extends React.Component {
constructor() {
super();
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render(){
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Search City...',
value,
onChange: this.onChange
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
<br/>
</div>
)
}
}
export default Form;
I want to render only 5 items, otherwise, computer hangs while loading huge data. Is there any other autocomplete react npm package, since I want only cities and country list. i.e when city is inputted, automatically the city name must be suggested with its relevant country.Any solution or suggestion highly appreciated. Thanks in advance
i modified you're getSuggestions() method a little i guess this should work for you.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
).slice(0,5);
};
Use the Slice method with start index and last Index
suggestions={suggestions.slice(0, 5)}
import {
React
,Avatar
,axiosbase
} from '../../import-files';
import Autosuggest from 'react-autosuggest';
import './autosuggest.css';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Paper from '#material-ui/core/Paper';
import MenuItem from '#material-ui/core/MenuItem';
let suggestions = [ { label: 'Afghanistan' } ];
function renderInputComponent(inputProps) {
const { classes, inputRef = () => {}, ref, ...other } = inputProps;
return (
<TextField
className={classes.textField}
fullWidth
variant="outlined"
InputProps={{
inputRef: node => {
ref(node);
inputRef(node);
},
classes: {
input: classes.input,
},
}}
{...other}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
return (
<MenuItem selected={isHighlighted} component="div">
<div>
<strong key={String(suggestion.id)} style={{ fontWeight: 300 }}>
<span className="sugg-option">
<span className="icon-wrap">
<Avatar src={suggestion.Poster}></Avatar>
</span>
<span className="name">
{suggestion.Title}
</span>
</span>
</strong>
</div>
</MenuItem>
);
}
function initSuggestions(value) {
suggestions = value;
}
function getSuggestionValue(suggestion) {
return suggestion.Title;
}
function onSuggestionSelected(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) {
console.log('HandleSuggestion() '+suggestionValue);
}
const styles = theme => ({
root: {
height: 50,
flexGrow: 1,
},
container: {
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
zIndex: 998,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
overflowY: 'scroll',
maxHeight:'376%'
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
divider: {
height: theme.spacing.unit * 2,
},
});
class IntegrationAutosuggest extends React.Component {
state = {
single: '',
popper: '',
suggestions: [],
};
componentDidMount() {
initSuggestions(suggestions);
}
// Filter logic
getSuggestions = async (value) => {
const inputValue = value.trim().toLowerCase();
var _filter = JSON.stringify({
filter : inputValue,
});
return await axiosbase.post(`${apiCall}`, _filter);
};
handleSuggestionsFetchRequested = ({ value }) => {
this.getSuggestions(value)
.then(data => {
if (data.Error) {
this.setState({
suggestions: []
});
} else {
const responseData = [];
data.data.itemsList.map((item, i) => {
let File = {
id: item.idEnc,
Title: item.englishFullName +' '+item.arabicFullName,
englishFullName: item.englishFullName,
arabicFullName: item.arabicFullName,
Poster: item.photoPath,
}
responseData.push(File);
});
this.setState({
suggestions: responseData
});
}
})
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = name => (event, { newValue }) => {
this.setState({
[name]: newValue,
});
if(event.type=='click'){
if(typeof this.props.handleOrderUserFirstNameChange === "function"){
this.props.handleOrderUserFirstNameChange(newValue);
}
this.state.suggestions.filter(f=>f.Title===newValue).map((item, i) => {
//id
//Title
// Poster
if(typeof this.props.handleUserIDChange === "function"){
this.props.handleUserIDChange(item.id);
}
});
}
};
render() {
const { classes } = this.props;
// console.log('Re-render!!');
// console.log(this.props);
// console.log(this.state.suggestions);
const autosuggestProps = {
renderInputComponent,
suggestions: this.state.suggestions,
onSuggestionsFetchRequested: this.handleSuggestionsFetchRequested,
onSuggestionsClearRequested: this.handleSuggestionsClearRequested,
onSuggestionSelected: this.props.onSelect,
getSuggestionValue,
renderSuggestion,
};
return (
<div className={classes.root}>
<Autosuggest
{...autosuggestProps}
inputProps={{
classes,
placeholder: this.props.placeHolder,
value: this.state.single,
onChange: this.handleChange('single'),
}}
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderSuggestionsContainer={options => (
<Paper {...options.containerProps} square>
{options.children}
</Paper>
)}
/>
<div className={classes.divider} />
</div>
);
}
}
export default withStyles(styles)(IntegrationAutosuggest);

React props of parent are not passed to child component

I'm passing props to a child component, from a parent component that receive this same prop from his own parent.
For some reason, when the parent props get updated, this update does not affect the child component.
The component in itself is very basic:
here's the parent :
import React, { Component } from 'react'
import styled from 'styled-components'
import { Icon } from 'antd'
import BaseProductSelector from '../BaseProductSelector'
import BaseProductPreview from '../BaseProductPreview'
import FullDesignSelector from '../FullDesignSelector'
import ColorPicker from '../ColorPicker'
import GeneratorProgress from '../GeneratorProgress'
import GeneratorError from '../GeneratorError'
import BPDetails from './BPDetails'
const GeneratorFlow = styled.div`
/*background-color: #eeeeee;*/
padding: 0 60px;
h3 {
color: #444;
}
.innerGenFlow {
padding: 15px;
border-radius: 5px;
box-shadow: 0 20px 40px -14px rgba(0, 0, 0, 0.35);
}
`
export default class ProductGeneratorFlow extends Component {
constructor(props) {
super()
let colors
if (props.product) {
colors = props.product.variations.filter(p => p.type === 'color')
} else {
colors = []
// colors.push(props.baseProduct.variations.filter(p => p.type === 'color')[0])
}
this.state = {
pickedColors: colors,
done: false,
error: false,
}
this.setStep = this.setStep.bind(this)
this.setKey = this.setKey.bind(this)
this.showBP = this.showBP.bind(this)
this.hideBP = this.hideBP.bind(this)
this.onChange = this.onChange.bind(this)
this.toggleColor = this.toggleColor.bind(this)
this.toggleAll = this.toggleAll.bind(this)
this.productCreated = this.productCreated.bind(this)
this.productPending = this.productPending.bind(this)
this.setFirstColor = this.setFirstColor.bind(this)
this.displayError = this.displayError.bind(this)
}
setStep(step) {
this.setState({ step })
}
showBP() {
this.setState({ BPDisplayed: true })
}
hideBP() {
this.setState({ BPDisplayed: false })
}
getBaseProd() {
const bpid = this.props.product.supplierBaseProductId
const result = this.props.base_products.filter(obj => obj._id === bpid)
return result[0]
}
setKey(activeKey) {
this.setState({
activeKey,
})
}
onChange(activeKey) {
this.setState({
activeKey,
})
}
productPending() {
this.setState({
done: false,
error: false,
})
this.props.showBP()
}
productCreated() {
this.props.displaySuccess()
this.setState({ done: true })
}
displayError() {
this.setState({ error: true })
}
toggleColor(color) {
let pickedColors = this.state.pickedColors
if (this.state.pickedColors.includes(color)) {
// console.log(pickedColors.filter(i => i != color).length)
pickedColors = pickedColors.filter(i => i != color)
} else {
pickedColors.push(color)
}
this.setState({
pickedColors,
})
}
test(id) {
this.setState({picked: true})
this.props.select(id)
}
toggleAll(value) {
if (value === true) {
this.setState({
pickedColors: this.props.baseProduct.variations.filter(p => p.type === 'color'),
})
} else {
this.setState({ pickedColors: [] })
}
}
setFirstColor() {
if (this.state.pickedColors.length > 0) {
this.props.setVariation(this.state.pickedColors[0])
}
}
render() {
if (this.state.error) {
return (
<GeneratorError
showBP={this.props.showBP}
reset={this.productPending}
/>
)
}
if (this.state.done) {
return (
<GeneratorProgress
active
showBP={this.props.showBP}
reset={this.productPending}
/>
)
}
if (this.props.product) {
return (
<GeneratorFlow>
<FullDesignSelector
designs={this.props.designs}
select={this.test}
addedDesigns={this.props.addedDesigns}
getImage={this.props.getImage}
removeDesign={this.props.removeDesign}
active
printingZone={this.props.printingZone}
setStep={this.setStep}
showBP={this.showBP}
showDS={this.props.showDS}
setKey={this.setKey}
/>
<ColorPicker
baseProduct={this.props.baseProduct}
product={this.props.product}
picked={this.state.pickedColors}
toggleColor={this.toggleColor}
variation={this.props.variation}
selectAll={this.toggleAll}
toggleFirstColor={this.props.toggleFirstColor}
setVariation={this.props.setVariation}
selectedColor={
this.props.variation ? this.props.variation.value : null
}
setPreviewColor={this.props.setVariation}
/>
<BaseProductPreview
addedDesigns={this.props.addedDesigns}
size={this.props.size}
shop={this.props.shop}
printingZone={this.props.printingZone}
picked={this.state.pickedColors}
previews={this.props.previews}
product={this.props.product}
setDone={this.productCreated}
baseProduct={this.getBaseProd()}
displaySuccess={this.props.displaySuccess}
generatorError={this.displayError}
status='edition'
setKey={this.setKey}
products={this.props.products}
productLoading={this.props.productLoading}
/>
</GeneratorFlow>
)
}
return (
<GeneratorFlow>
<ColorPicker
picked={this.state.pickedColors}
toggleColor={this.toggleColor}
baseProduct={this.props.baseProduct}
toggleFirstColor={this.setFirstColor}
variation={this.props.variation}
selectAll={this.toggleAll}
setVariation={this.props.setVariation}
selectedColor={
this.props.variation ? this.props.variation.value : null
}
setPreviewColor={this.props.setVariation}
/>
<FullDesignSelector
designs={this.props.designs}
select={this.props.select}
addedDesigns={this.props.addedDesigns}
getImage={this.props.getImage}
printingZone={this.props.printingZone}
removeDesign={this.props.removeDesign}
active
setStep={this.setStep}
showBP={this.showBP}
showDS={this.props.showDS}
setKey={this.setKey}
/>
<BaseProductPreview
addedDesigns={this.props.addedDesigns}
baseProduct={this.props.baseProduct}
generatorError={this.displayError}
size={this.props.size}
displaySuccess={this.props.displaySuccess}
shop={this.props.shop}
picked={this.state.pickedColors}
setDone={this.productCreated}
printingZone={this.props.printingZone}
previews={this.props.previews}
setPreview={this.props.setPreview}
status='creation'
setStep={this.setStep}
products={this.props.products}
productLoading={this.props.productLoading}
/>
</GeneratorFlow>
)
}
}
And here is the only part thst uses this prop in the child
import React, { Component } from 'react'
import styled from 'styled-components'
import toPx from 'unit-to-px'
import _ from 'lodash'
import { LocalForm, Control } from 'react-redux-form'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { toast } from 'react-toastify'
import { Input, Select, Icon, Tooltip } from 'antd'
import s3 from '../../../../../services/s3'
import theme from '../../../../../theme/theme'
import Alert from '../../../../../components/Alert'
import { createProduct, modifyProduct } from '../../../../../modules/products'
// import ProductImage from '../../../../../components/ProductImage'
class BaseProductPreview extends Component {
constructor(props) {
super(props)
let colors
if (props.product) {
colors = Object.assign([], props.product.variations.filter(v => v.type === 'color'))
} else {
colors = []
}
this.state = {
name: props.product ? props.product.name : '',
displayDescription: props.product ? Object.assign({}, props.product.displayDescription) : {},
collections: (props.product && props.product.collections) ? props.product.collections : [],
pricing: props.product ? Object.assign({}, props.product.pricing) : { margin: 0 },
elevdone: false,
i: 0,
colors,
}
this.createInnerProduct = this.createInnerProduct.bind(this)
this.getPrice = this.getPrice.bind(this)
}
oneColor() {
if (this.props.baseProduct.variations && this.state.colors) {
let colorAlert
if (this.state.colorAlertShown) {
colorAlert = <Alert message='Choisissez au moins une couleur' type='error' />
}
const colorsBp = this.props.baseProduct.variations.filter(v => v.type === 'color')
if (colorsBp.length <= 1) {
return ''
}
return (
<div>
<p>Choix de couleur :</p>
{ colorAlert }
<span
className='bullet-color'
>
{this.getColorsRef(this.props.baseProduct).map(value =>
(<div
onClick={() => { this.toggleColor(value) }}
className={this.colorIsInProduct(value)}
style={{ backgroundColor: value }}
/>))}
</span>
</div>)
}
return null
}
getColorsRef() {
return this.props.baseProduct.variations.filter(v => v.type === 'color').map(a => a.value)
}
colorIsInProduct(couleur) {
// true/false
let active
if (this.state.colors.find(v => v.value === couleur)) {
active = 'active-color'
}
return active
}
toggleColor(couleur) {
// const item = this.state.item
const colors = this.state.colors
// si on a deja la couleur dans le produit on l'enlève
if (colors.find(v => v.value === couleur) && colors.length > 1) {
// je retire cette couleur des varaitions de mon produits
const index = colors.indexOf(colors.find(v => v.value === couleur))
colors.splice(index, 1)
} else if (colors.find(v => v.value === couleur)) {
this.setState({ colorAlertShown: true })
} else {
// on va chercher la variation couleur corespondante
// dans le base product et on la copie dans le product
this.setState({ colorAlertShown: false })
colors.push(this.props.baseProduct.variations.find(v => v.value === couleur))
}
this.setState({ colors })
// TODO on change la couleur du mockup
}
getJsonsObjects(printingZones) {
// INITIATE EMPTY JSON OBJECT
const jsonsArray = {}
// GET CURRENT CANVAS
printingZones.map((item) => {
const y = document.getElementById(`${item}-canvas`).fabric
const helper = _.filter(y.getObjects(), { clipFor: 'layer' })[0]
if (helper) {
helper.set({ stroke: 'transparent' })
}
jsonsArray[item] = y.toJSON(['height'])
})
return jsonsArray
}
getCustomizationPrice() {
let customizationPrice = 0
Object.keys(this.props.baseProduct.printingZone).map((item) => {
const y = document.getElementById(`${item}-canvas`).fabric
const items = y.getObjects()
if (items.length > 1) {
customizationPrice = customizationPrice + 5
}
})
customizationPrice = customizationPrice - 5
if (customizationPrice < 0) {
customizationPrice = 0
}
return customizationPrice
}
getAction() {
return (<p>Créer mon produit</p>)
}
marginValidation(value) {
let returned_value = value
if (value == '') {
returned_value = 0
}
if (!value) {
returned_value = 0
} else if (value > 100) {
// TODO Show moreThan100Alert
returned_value = 100
}
const pricing = Object.assign({}, this.state.pricing, { margin: returned_value })
this.setState({ pricing })
}
validForm() {
if (this.state.name && this.props.picked.length > 0 && this.state.pricing.margin >= 0 && this.props.addedDesigns.length > 0) {
return false
}
return true
}
getPrice() {
const position_print = this.props.addedDesigns.map(d => {
return d.position
})
// uniq(position_print)
const count = []
position_print.map((position) => {
if (count.indexOf(position) === -1) {
count.push(position)
}
})
if (count.length <= 1) {
return (
<div>
<p className='price'>Cout de production <span>{this.props.baseProduct.unitPrice} €</span></p>
<div className='price-marge'>Vos bénéfices <span className='requiredField2'>*</span>
<Control.text
component={Input}
className='inputMarge'
model='.margin'
value={this.state.pricing.margin}
onChange={(e) => {
this.marginValidation(e.target.value.replace(',', '.'))
}}
/>
</div>
<hr />
<div className='price-total'>
{`
${parseFloat(this.props.baseProduct.unitPrice)
+
parseFloat(this.state.pricing.margin)} €`}
</div>
</div>
)
}
if (count.length > 1) {
return (
<div>
<p className='price'>Cout de production <span>{this.props.baseProduct.unitPrice} €</span></p>
<p className='price'>Impression supplémentaire <span>5 €</span></p>
<div className='price-marge'>Vos bénéfices <span className='requiredField2'>*</span>
<Control.text
component={Input}
className='inputMarge'
model='.margin'
value={this.state.pricing.margin}
onChange={(e) => {
this.marginValidation(e.target.value.replace(',', '.'))
}}
/>
</div>
<hr />
<div className='price-total'>
{`
${parseFloat(this.props.baseProduct.unitPrice)
+
parseFloat(this.state.pricing.margin) + parseFloat(5)} €`}
</div>
</div>
)
}
return null
}
getCategory() {
if (this.props.baseProduct.category.fr[0] === 'Homme' && this.props.baseProduct.category.fr[1] === 'Femme') {
return (<span>Unisex</span>)
}
if (this.props.baseProduct.category.fr[0] === 'Homme') {
return (<span>Homme</span>)
}
if (this.props.baseProduct.category.fr[0] === 'Femme') {
return (<span>Femme</span>)
}
return null
}
showElevio() {
if (this.state.i < 5) {
this.state.i = this.state.i + 1
setTimeout(() => {
if (this.props.productLoading.loading === true || this.props.products.length === 0) {
if (this.props.products.length === 0 && this.state.elevdone === false) {
return (
window._elev.openArticle(263),
this.setState({ elevdone: true })
)
} return null
}
if (this.props.productLoading.loading === false) {
this.showElevio()
}
return null
}, 500)
} return null
}
render() {
const { Option } = Select
const children = []
if (this.props.shop.settings.collections) {
this.props.shop.settings.collections.map((collec, i) => {
children.push(<Option key={collec.name ? collec.name : i}>{collec.name}</Option>)
return null
})
}
this.showElevio()
return (
<StyledBaseProductPreview>
<h2>Description</h2>
<LocalForm
onSubmit={() => this.createInnerProduct()}
>
<div className='form-step'>
<p className='advice-name'>
<Tooltip title='Figurera sur la fiche produit'>
<span>{this.props.baseProduct.subCategory.fr} {this.getCategory()}</span>
</Tooltip>
</p>
<p>Nom <span className='requiredField'>*</span></p>
<Control.text
component={Input}
model='.name'
placeholder='Nom du produit'
value={this.state.name}
onChange={(e) => {
this.setState({ name: e.target.value })
}}
/>
</div>
<div className='form-step'>
<p>Description</p>
<Control.textarea
className='productDescription'
model='.displayDescription'
placeholder='Description du produit'
value={this.state.displayDescription.fr}
onChange={(e) => {
const new_item = Object.assign({}, this.state.displayDescription)
new_item.fr = e.target.value
this.setState({ displayDescription: new_item })
}}
/>
</div>
<div className='form-step'>
<p>Collection(s)</p>
<Select
mode='multiple'
className='styledSelect'
placeholder='Pas de collection'
notFoundContent='Pas de collection'
value={this.state.collections}
style={{ width: '100%' }}
onSearch={(e) => {
this.setState({ toCreate: e })
}}
onChange={(e) => {
this.setState({ collections: e })
}}
>
{children}
</Select>
</div>
<div className='form-step pricingForm'>
<h2>Prix </h2>
<hr />
{this.getPrice()}
</div>
<Crumpet type='submit' className='superCrumpet' disabled={this.validForm()}>
{this.getAction()}
</Crumpet>
</LocalForm>
</StyledBaseProductPreview>
)
}
}
const mapStateToProps = (state, ownProps) => {
console.log(state); // state
console.log(ownProps); // undefined
return({
user: state.user,
addedDesigns: ownProps.addedDesigns,
})
}
const mapDispatchToProps = dispatch => bindActionCreators({
createProduct,
modifyProduct,
}, dispatch)
export default connect(
mapStateToProps,
mapDispatchToProps,
)(BaseProductPreview)
When inspecting these elements, I can see the parent getting updated :
And now, the funny part: when parent props get updated, the first two child component props are updated as well, but BaseProductPreview doesn't !
However, this gets updated as soon as I change the state of the child component.
How comes so ? How can the state update the component props ?
React only act on state not on props. whenever you change state UI gets update with latest changes. bind your props to state in child component and use componentWillReceiveProps(props) to change child state when props get updated in parent component.
Well the only solution I found was to connect a higher component with redux, so that none of the children were redux-related anymore, and then pass the reducer function through props to that child.
Without redux, my component gets updated properly.

Resources