I'm using react functional components inside that, I have two different array objects. I would like to render the array objects based on the condition which I have. I'm using the function hooks to define a reference and assign to it dynamically. But, this is returning null. can someone help me on this ? In the below code isDemandGraph is returning null while rendering.
function gridHeatMap(props) {
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
const isDemandGraph = useRef(null);
const { height, title, viewName } = props;
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{viewName === 'demand' ? { isDemandGraph: heatMap } : { isDemandGraph: demandHeatMap }}
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</Grid>
</div>
)
It won't work that way, calling the isDemandGraph inside the ternary operator does not assign value to the same.
Also, you should use useState instead of useRef for your usecase.
I am adding the updated code snippet, it should work as expected:
function gridHeatMap(props) {
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
const { height, title, viewName } = props;
const [isDemandGraph, setIsDemandGraph ] = useState(viewName === 'demand' ? heatMap : demandHeatMap);
// setIsDemandGraph can be used to update the value of `isDemandGraph`
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</div>
I suggest you to use useState instead of useRef and use useEffect to listen prop changes like:
function gridHeatMap(props) {
const { height, title, viewName } = props;
const [isDemandGraph, setisDemandGraph] = useState([]);
const classes = useStyles();
const heatMap = [
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
];
const demandHeatMap = heatMap.reverse();
useEffect(() => {
viewName === 'demand' ? setisDemandGraph(heatMap) : setisDemandGraph(demandHeatMap);
}, [viewName]);
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<Grid container spacing={0}>
{isDemandGraph.map((item, index) => {
return (
<Grid item style={{ flexGrow: '1', height, width: '11%' }} className={item.color}>
</Grid>
</div>
)
I think this is how I would go (please notice I modified a bit the returned JSX, as the one in your code did not look correct to me, so you might want to double check it):
import React, { useState, useMemo, useEffect } from 'react';
// ... other imports
function gridHeatMap({ height, title, viewName }) {
const heatMap = useMemo(() => ([
{ color: 'redVeryHigh', value: 50 },
{ color: 'redHigh', value: 25 },
{ color: 'redMedium', value: 20 },
{ color: 'redLow', value: 15 },
{ color: 'white', value: 0 },
{ color: 'greenLow', value: 15 },
{ color: 'greenMedium', value: 20 },
{ color: 'greenHigh', value: 25 },
{ color: 'greenVeryHigh', value: 50 },
]), []);
const [currentHeatMap, setCurrentHeatMap] = useState(heatMap);
useEffect(() => {
viewName === 'demand'
? setCurrentHeatMap(heatMap)
: setCurrentHeatMap(heatMap.reverse());
}, [viewName]);
const classes = useStyles();
return (
<div className={classes.root}>
<div className={classes.title}>{title}</div>
<GridContainer spacing={0}>
{
currentHeatMap.map((item, index) => (
<Grid
key={`grid-${index}-${item.color}`}
className={item.color}
style={{ flexGrow: '1', height, width: '11%' }}
item
/>
));
}
</GridContainer>
</div>
);
}
export default gridHeatMap;
Related
I have a working prototype where I cant seem to figure out when I click on a particular button in my nav, then pass that index to another component. It doesn't update the index when I query using "document.querySelector". My goal is to just move the content to the front child position/firstChild depending on what button you clicked. So if you clicked the first button then the last, my last content would be first and so and so on...I've tried a timeout and still no luck.
JS:
.....
const Context = createContext(false);
const dataArr = [
{
id: "113062",
name: "Blue",
region: "US",
sort: "1"
},
{
id: "115102",
name: "Red",
region: "DE",
sort: "2"
},
{
id: "70884",
name: "Green",
region: "US",
sort: "3"
},
{
id: "114683",
name: "Yellow",
region: "US",
sort: "4"
},
{
id: "112878",
name: "Pale",
region: "US",
sort: "5"
},
{
id: "114682",
name: "Orange",
region: "US",
sort: "6"
},
{
id: "120093",
name: "Black",
region: "CH",
sort: "8"
},
{
id: "120594",
name: "White",
region: "CH",
sort: "9"
}
];
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
"& > *": {
margin: theme.spacing(1)
}
},
grey: {
border: "4px solid white"
},
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
border: "4px solid black"
},
info: {
margin: "10px"
},
wrapper: {
display: "flex"
},
contentWrapper: {
display: "flex",
flexDirection: "column"
},
elWrapper: {
opacity: 0,
"&.active": {
opacity: 1
}
}
}));
const ToggleItem = ({ id, description }) => {
const { handleChange, selected, classes } = useContext(Context);
const isSelected = selected.includes(description);
const handleClick = (idx) => {
console.log("idx====", idx);
handleChange(description, idx);
};
return (
<>
<Avatar
className={isSelected ? classes.orange : classes.grey}
onClick={() => handleClick(id)}
>
<span style={{ fontSize: ".75rem" }}>{description}</span>
</Avatar>
</>
);
};
const ToggleContainer = ({ selected, list }) => {
const isSelected = list.filter((element) => selected.includes(element));
return (
<>
{dataArr.map((item, idx) => (
<div
key={idx}
className="carouselWrapper"
style={{ display: isSelected.includes(item.name) ? "block" : "none" }}
>
{item.name}
</div>
))}
</>
);
};
const ToggleWrapper = () => {
const data = [];
dataArr.map((el) => data.push(el.name));
const classes = useStyles();
const [selected, setSelected] = useState([]);
const [viewAll, setViewAll] = useState(true);
const handleChange = (val, idx) => {
setViewAll(false);
setSelected((selected) => {
if (selected.length === 1 && selected.includes(val)) {
return handleViewAll();
}
if (selected.includes(val)) {
return selected.filter((v) => v !== val);
} else {
return [val, ...selected];
}
});
setTimeout(() => {
const someParentObject = document.getElementById("contentWrapper");
const someChildObject = document.querySelectorAll(".carouselWrapper")[
idx
];
// eslint-disable-next-line no-unused-expressions
selected.length > 0 &&
someParentObject.insertBefore(
someChildObject,
someParentObject.firstChild
);
console.log(
"someParentObject.children[0]==",
someParentObject.firstChild
);
console.log("someChildObject", someChildObject);
}, 2000);
};
const handleViewAll = () => {
setViewAll(true);
setSelected([]);
};
return (
<Context.Provider
value={{
viewAll,
handleChange,
handleViewAll,
selected,
classes
}}
>
<div className={classes.wrapper}>
<Avatar
className={viewAll ? classes.orange : null}
onClick={handleViewAll}
>
<span style={{ fontSize: "0.75rem", textAlign: "center" }}>
View All
</span>
</Avatar>
{dataArr.map((d, id) => {
return (
<div key={id}>
<ToggleItem id={id} description={d.name} />
</div>
);
})}
</div>
<div id="contentWrapper" className={classes.contentWrapper}>
<ToggleContainer selected={viewAll ? data : selected} list={data} />
</div>
</Context.Provider>
);
};
export default function App() {
return <ToggleWrapper />;
}
Here is a very quick example of one option. It stores the data in a Map which allows for efficient retrieval (this could be moved to context to avoid passing it to multiple child components) and tracks selected by id in a separate state array. It's then just a matter of mapping the selected array to get your ordered list. If selected is empty render the full values() of the Map.
const { useState, useEffect } = React;
function App({ data }) {
const [selected, setSelected] = useState([]);
const [itemMap, setItemMap] = useState(new Map());
useEffect(() => {
setItemMap(new Map(data.map(({ id, ...rest }) => [id, { id, ...rest }])));
}, [data]);
const selectHandler = (id) => {
setSelected(selected => {
if (id === undefined) return [];
return selected.includes(id)
? selected.filter(_id => _id !== id)
: [id, ...selected];
});
}
return (
<div>
<Header itemMap={itemMap} selected={selected} selectHandler={selectHandler} />
<Selected itemMap={itemMap} selected={selected} />
</div>
);
}
function Header({ itemMap, selected, selectHandler }) {
return (
<div className={"header-container"}>
<div className={!selected.length && 'selected'} onClick={() => selectHandler()}>All</div>
{[...itemMap.values()].map(({ id, name }) => (
<div key={id} className={selected.includes(id) && 'selected'} onClick={() => selectHandler(id)}>
{name}
</div>
))}
</div>
);
}
function Selected({ itemMap, selected }) {
return (
<div className={"selected-container"}>
{selected.length
? selected.map((id) => (
<div key={id} >
{itemMap.get(id).name}
</div>
))
: [...itemMap.values()].map(({ id, name }) => (
<div key={id} >
{name}
</div>
))
}
</div>
);
}
const dataArr = [{ id: "113062", name: "Blue", region: "US", sort: "1" }, { id: "115102", name: "Red", region: "DE", sort: "2" }, { id: "70884", name: "Green", region: "US", sort: "3" }, { id: "114683", name: "Yellow", region: "US", sort: "4" }, { id: "112878", name: "Pale", region: "US", sort: "5" }, { id: "114682", name: "Orange", region: "US", sort: "6" }, { id: "120093", name: "Black", region: "CH", sort: "8" }, { id: "120594", name: "White", region: "CH", sort: "9" }];
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App data={dataArr} />);
.header-container { width: 100vw; display: flex; justify-content: flex-start;}
.header-container div { width: 40px; height: 40px; display: flex; overflow: hidden; position: relative; margin: 4px; font-size:.75rem; align-items: center; user-select: none; border-radius: 50%; justify-content: center; color: #fafafa; background-color: #bdbdbd;}
.header-container div.selected { background-color: tomato;}
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id='root'></div>
I have a React native function Quiz with them where I make a fetch GET request. I have a check if the data has loaded with the showQuestion variable. But somehow it, when the data has loaded the app, returns this error message:
Cannot read properties of undefined (evaluating
'questObj[currentQuestion].questionText')
I tried to print the data questObj[currentQuestion] and that gives me an object which is filled with data. The image down below is the result of console.log.
console.log(questObj[currentQuestion]);
Any suggestions on what I could be doing wrong? (I'm new to React native so it could probably be something I'm doing completely wrong.)
export function Quiz(this: any) {
const styles = StyleSheet.create({
container: {
backgroundColor: '#B86566',
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center'
},
headingColumn: {
flexBasis: '90%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flex: 1,
padding: 20,
fontSize: 30,
},
buttonColumn: {
flexBasis: '35%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
flex: 1,
padding: 20,
margin: 20,
borderColor: 'white',
borderWidth: 1
},
row: {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
width: '100%'
},
});
const [showScore, setShowScore] = useState(false);
const [showQuestion, setShowQuestion] = useState(false);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [score, setScore] = useState(0);
const [questionArray, setQuestions] = useState(0);
useEffect(() => {
getQuestions();
}, []);
let questObj: {
questionText: any;
answerOptions: any;
}[] = [];
const getQuestions = async () => {
try {
let response = await fetch(
'https://opentdb.com/api.php?amount=10&category=26&difficulty=medium&type=multiple'
);
let json = await response.json();
json.results.forEach((element: { question: any; correct_answer: any; incorrect_answers: any; }) => {
questObj.push({
questionText: element.question,
answerOptions: [{ answerText: element.correct_answer, isCorrect: true },
{ answerText: element.incorrect_answers[0], isCorrect: false },
{ answerText: element.incorrect_answers[1], isCorrect: false },
{ answerText: element.incorrect_answers[2], isCorrect: false }
],
},
);
console.log(questObj[currentQuestion]);
setShowQuestion(true);
});
} catch (error) {
console.error(error);
}
}
const handleAnswerButtonClick = (isCorrect: boolean) => {
if (isCorrect) {
setScore(score + 1);
}
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questObj.length) {
setCurrentQuestion(nextQuestion);
} else {
setShowScore(true);
}
};
return <View style={styles.container}>
<View><Text></Text></View>
<View>{showScore ?
<Text>You scored {score} out of {questObj.length}</Text>
: <></>}
</View>
{showQuestion ?
<View>
<View >
<Text>Question {currentQuestion + 1}/{questObj.length} </Text>
</View>
<View style={styles.row}>
<Text style={Object.assign(styles.headingColumn)}>{questObj[currentQuestion].questionText}</Text>
</View>
<View style={styles.row}>
{questObj[currentQuestion].answerOptions.map((answerOption: { answerText: string; isCorrect: boolean; }, index: any) => (
<Text style={Object.assign(styles.buttonColumn)}>
<Button
title={answerOption.answerText}
onPress={() => handleAnswerButtonClick(answerOption.isCorrect)}
color="#fff">
</Button>
</Text>
))}
</View>
</View>
: <></>}
</View>;
}
When using react, you never want to grab data from a source and manually try to control the state. That's what useState() is for :).
Any kind of data that you are dealing with (for the most part with a few exceptions) should all be handled and maintained with some sort of useState() hook.
By not using useState(), React will not know that the data you are wanting to render in the dom has been updated with actual data!
So when you call questObj[currentQuestion].questionText inside of the JSX, react sees no data (since you only pushed data onto an array and didn't trigger a proper re-render), and says "HEY, there is no data here as an object".
Also, be sure that you are properly gathering the right state for choosing the currentQuestion. If you are only choosing one, you have to loop through an array of objects to then identify an object key that matches your current question... unless you are simply going off of array indexes, then it should be fine I suppose!
Good Luck!
Below is a fixing of the useState(), or lack there of.
export function Quiz(this: any) {
const styles = StyleSheet.create({
container: {
backgroundColor: "#B86566",
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
},
headingColumn: {
flexBasis: "90%",
display: "flex",
flexDirection: "column",
alignItems: "center",
flex: 1,
padding: 20,
fontSize: 30,
},
buttonColumn: {
flexBasis: "35%",
display: "flex",
flexDirection: "column",
alignItems: "center",
flex: 1,
padding: 20,
margin: 20,
borderColor: "white",
borderWidth: 1,
},
row: {
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
width: "100%",
},
});
const [showScore, setShowScore] = useState(false);
const [showQuestion, setShowQuestion] = useState(false);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [score, setScore] = useState(0);
const [questionArray, setQuestions] = useState(0);
useEffect(() => {
getQuestions();
}, []);
// let questObj: {
// questionText: any;
// answerOptions: any;
// }[] = [];
// you need to use Use state for any state that you are holding! That's the whole point of react!
const [questObjState, setQuestObjState] = useState([]);
const getQuestions = async() => {
try {
let response = await fetch(
"https://opentdb.com/api.php?amount=10&category=26&difficulty=medium&type=multiple"
);
let json = await response.json();
// use map here to return new array rather than just opertate
const newArrayOfData = json.results.map(
(element: {
question: any,
correct_answer: any,
incorrect_answers: any,
}) => {
return {
questionText: element.question,
answerOptions: [{
answerText: element.correct_answer,
isCorrect: true,
},
{
answerText: element.incorrect_answers[0],
isCorrect: false,
},
{
answerText: element.incorrect_answers[1],
isCorrect: false,
},
{
answerText: element.incorrect_answers[2],
isCorrect: false,
},
],
};
// questObj.push({
// questionText: element.question,
// answerOptions: [
// {
// answerText: element.correct_answer,
// isCorrect: true,
// },
// {
// answerText: element.incorrect_answers[0],
// isCorrect: false,
// },
// {
// answerText: element.incorrect_answers[1],
// isCorrect: false,
// },
// {
// answerText: element.incorrect_answers[2],
// isCorrect: false,
// },
// ],
// });
// console.log(questObj[currentQuestion]);
// setShowQuestion(true);
},
setQuestObjState(newArrayOfData)
);
} catch (error) {
console.error(error);
}
};
const handleAnswerButtonClick = (isCorrect: boolean) => {
if (isCorrect) {
setScore(score + 1);
}
const nextQuestion = currentQuestion + 1;
if (nextQuestion < questObj.length) {
setCurrentQuestion(nextQuestion);
} else {
setShowScore(true);
}
};
return ( <
View style = {
styles.container
} >
<
View >
<
Text > < /Text> <
/View> <
View > {
showScore ? ( <
Text >
You scored {
score
}
out of {
questObj.length
} <
/Text>
) : ( <
> < />
)
} <
/View> {
showQuestion ? ( <
View >
<
View >
<
Text >
Question {
currentQuestion + 1
}
/{questObj.length}{" "} <
/Text> <
/View>
<
View style = {
styles.row
} >
<
Text style = {
Object.assign(styles.headingColumn)
} > {
questObj[currentQuestion].questionText
} <
/Text> <
/View> <
View style = {
styles.row
} > {
questObj[currentQuestion].answerOptions.map(
(
answerOption: {
answerText: string,
isCorrect: boolean,
},
index: any
) => ( <
Text style = {
Object.assign(styles.buttonColumn)
} >
<
Button title = {
answerOption.answerText
}
onPress = {
() =>
handleAnswerButtonClick(
answerOption.isCorrect
)
}
color = "#fff" >
< /Button> <
/Text>
)
)
} <
/View> <
/View>
) : ( <
> < />
)
} <
/View>
);
}
https://stackoverflow.com/questions/70712396/error-undefined-is-not-an-object-in-react-native#
I used the code from this article as an example.
It is possible to read the data, but not to save it.
Code:
const { Component, Fragment } = wp.element;
const {
RichText,
InspectorControls,
PanelColorSettings,
AlignmentToolbar,
BlockControls,
} = wp.editor;
const { Button, PanelBody, SelectControl, TextControl } = wp.components;
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { withSelect, withDispatch } = wp.data;
class Inspector extends Component {
constructor(props) {
super(...arguments);
}
render() {
const backgroundColors = [
{ color: "#525252", name: "Черный" },
{ color: "#872d2d", name: "Акцентный красный" },
{ color: "#e49312", name: "Акцентный желтый" },
{ color: "#bab3a6", name: "Акцентный кремовый" },
];
const fontSizeOptions = [
{ value: "14px", label: __("14px") },
{ value: "16px", label: __("16px") },
{ value: "18px", label: __("18px") },
{ value: "22px", label: __("22px") },
{ value: "28px", label: __("28px") },
];
const paddingTopOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const paddingBottomOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const {
setAttributes,
attributes: { text_color, font_size, padding_top, padding_bottom },
} = this.props;
let PluginMetaFields = (props) => {
return (
<>
<TextControl
value={props.text_metafield}
label={__("Text Meta", "textdomain")}
onChange={(value) => props.onMetaFieldChange(value)}
/>
</>
);
};
PluginMetaFields = withSelect((select) => {
return {
text_metafield: select("core/editor").getEditedPostAttribute("meta")[
"_myprefix_text_metafield"
],
};
})(PluginMetaFields);
PluginMetaFields = withDispatch((dispatch) => {
return {
onMetaFieldChange: (value) => {
dispatch("core/editor").editPost({
meta: { _myprefix_text_metafield: value },
});
},
};
})(PluginMetaFields);
return (
<InspectorControls key="inspector">
<PanelBody title={__("Настройки абзаца")}>
<PanelColorSettings
title={__("Цвет шрифта")}
initialOpen={true}
colorSettings={[
{
value: text_color,
colors: backgroundColors,
onChange: (value) => setAttributes({ text_color: value }),
label: __("Цвет шрифта"),
},
]}
/>
<SelectControl
label={__("Размер шрифта")}
options={fontSizeOptions}
value={font_size}
onChange={(value) => this.props.setAttributes({ font_size: value })}
/>
<SelectControl
label={__("Отступ сверху")}
options={paddingTopOptions}
value={padding_top}
onChange={(value) =>
this.props.setAttributes({ padding_top: value })
}
/>
<SelectControl
label={__("Отступ снизу")}
options={paddingBottomOptions}
value={padding_bottom}
onChange={(value) =>
this.props.setAttributes({ padding_bottom: value })
}
/>
<PluginMetaFields />
</PanelBody>
</InspectorControls>
);
}
}
class HeadlineBlock extends Component {
render() {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
setAttributes,
} = this.props;
const onChangeAlignment = (newAlignment) => {
this.props.setAttributes({
alignment: newAlignment === undefined ? "none" : newAlignment,
});
};
return [
<Inspector {...{ setAttributes, ...this.props }} />,
<div>
{
<BlockControls>
<AlignmentToolbar value={alignment} onChange={onChangeAlignment} />
</BlockControls>
}
<RichText
tagName="p"
placeholder={__("Текст...")}
keepPlaceholderOnFocus
value={headline}
formattingControls={["bold", "italic", "strikethrough", "link"]}
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
textAlign: alignment,
}}
onChange={(value) => setAttributes({ headline: value })}
/>
</div>,
];
}
}
registerBlockType("amm-custom-block/test-block", {
title: __("Тест блок"),
icon: "shield",
category: "AMM",
attributes: {
headline: {
type: "string",
},
alignment: {
type: "string",
default: "none",
},
text_color: {
type: "string",
default: "#525252",
},
font_size: {
type: "string",
default: "14px",
},
padding_top: {
type: "string",
default: "50px",
},
padding_bottom: {
type: "string",
default: "0px",
},
},
edit: HeadlineBlock,
save: function (props) {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
} = props;
return (
<Fragment>
{headline && !!headline.length && (
<RichText.Content
tagName="p"
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
paddingTop: padding_top,
paddingBottom: padding_bottom,
textAlign: alignment,
}}
value={headline}
/>
)}
</Fragment>
);
},
});
So far just added a text field to the block and am trying to read and save the data.
With reading everything is OK, but saving the data does not work and there are no errors.
Any idea why this is happening?
sorry english is not a native language
The meta field _myprefix_text_metafield is a protected field as it starts with a "_" (underscore). This is why you can read the value in withSelect() but not save over it withDispatch() without passing auth_callback.
To save to a protected field, the auth_callback is required, eg:
<?php
register_post_meta( 'post', '_myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
})
);
?>
There is an example in the tutorial you are following:
https://css-tricks.com/managing-wordpress-metadata-in-gutenberg-using-a-sidebar-plugin/#post-291605
Alternatively, if your meta field is not required to be protected/private: re-register your meta field as myprefix_text_metafield (no underscore in the name and no auth_callback) and your current code will work, eg:
<?php
register_post_meta( 'post', 'myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string'
);
?>
I have this component which is a filter for a table..
handleSearch function is responsible to update const filters... its work perfectly when dataIndex props is the same, but when it changes, filters value is backing to it's initial value, an empty array.
I can't manage to resolve it, I've already console log everything.
import React, { useState, } from "react";
import { SearchOutlined } from "#ant-design/icons";
import { Select, Button, Space } from "antd";
const TableFilter = (props) => {
const {
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus,
dataIndex,
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
} = props;
const [filters, setFilters] = useState([]);
const SelectFilter = (
<Select
style={{ width: 188, marginBottom: 8, display: "block" }}
type={filterType}
mode={filterMode}
name={dataIndex}
value={selectedKeys}
optionFilterProp="children"
placeholder={`Search ${dataIndex}`}
onFocus={FilterSelectOnFocus}
showSearch
onChange={(value) => setSelectedKeys(value ? value : [])}
getPopupContainer={(trigger) => trigger}
notFoundContent
>
{filterOptions?.map((type, key) => (
<Select.Option value={type.value} key={key}>
{type.label}
</Select.Option>
))}
</Select>
);
const defaultFilterTypes = [
{
type: "select",
element: SelectFilter,
},
];
const handleFilterType = () => {
const type = defaultFilterTypes.find((types) => types.type === filterType);
return type.element;
};
const handleSearch = () => {
console.log(filters) //is empty when dataIndex value change, when it's is the same it get the update value of the 75 line
confirm();
const newFilterValues = [...filters]
const index = newFilterValues.findIndex(newValue => newValue.searchedColumn === dataIndex)
if(index === -1){
newFilterValues.push({ searchText: selectedKeys, searchedColumn: dataIndex})
}
else{
newFilterValues[index] = {searchText: selectedKeys, searchedColumn: dataIndex}
}
setFilters(newFilterValues)
}
const handleReset = () => {
console.log('reset');
clearFilters();
setFilters({ searchText: "" });
setSelectedKeys([]);
};
return (
<div style={{ padding: 8 }}>
{handleFilterType()}
<Space>
<Button
type="primary"
onClick={() => handleSearch()}
icon={<SearchOutlined />}
size="small"
style={{ width: 90 }}
>
Search
</Button>
<Button
onClick={() => handleReset()}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</Space>
</div>
);
};
export default TableFilter;
Table Component
import React, { useEffect, useState } from "react";
import { Table } from "antd";
import { getTransactions } from "../../../../api/Transactions";
import { formatCnpjCpf, formatCurrency } from "../../../../utils/masks";
import TableFilter from "../../../../shared-components/ant-design/containers/TableFilters";
import { getPartnersAsOptions } from "../../../../api/Partners";
const Insider = (props) => {
const [data, setData] = useState([]);
const [paginationValues, setPaginationValues] = useState({
current: 1,
pageSize: 50,
total: 0,
position: ["topRight"],
});
const [partners, setPartners] = useState([{value: null, label: 'carregando...'}])
const context = "insider";
function getColumnSearchProps(
dataIndex,
filterType,
filterMode,
filterOptions,
FilterSelectOnFocus
) {
return {
filterDropdown: ({
setSelectedKeys,
selectedKeys,
confirm,
clearFilters,
}) => {
return (
<TableFilter
dataIndex={dataIndex}
filterType={filterType}
filterMode={filterMode}
filterOptions={filterOptions}
FilterSelectOnFocus={FilterSelectOnFocus}
setSelectedKeys={setSelectedKeys}
selectedKeys={selectedKeys}
confirm={confirm}
clearFilters={clearFilters}
/>
);
},
};
}
async function getPartners(){
if(partners.length > 2){
return
}
const response = await getPartnersAsOptions(paginationValues)
setPartners(response.data)
}
const columns = [
{
dataIndex: ["transactionType", "desc"],
title: "Tipo de Transação",
sorter: true,
key: "orderTransactionType",
...getColumnSearchProps("orderTransactionType"),
},
{
dataIndex: "transactionDate",
title: "Data Transação",
key: "orderTransactionDate",
sorter: true,
...getColumnSearchProps("orderTransactionDate"),
},
{
title: "Nome origem",
dataIndex: ["source", "name"],
sorter: true,
key: "orderSourceCustomerName",
},
{
render: (render) => formatCnpjCpf(render.source.document.value),
title: "Documento origem",
key: "sourceCustomer",
...getColumnSearchProps("sourceCustomer", "select", "tags")
},
{
title: "Nome destino",
dataIndex: ["target", "name"],
sorter: true,
key: "orderTargetCustomerName",
},
{
render: (render) => formatCnpjCpf(render.target.document.value),
title: "Documento destino",
},
{
render: (render) => formatCurrency(render.value),
title: "Valor da transação",
key: "orderValue",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.chargedTariff),
title: "Tarifa",
key: "orderChargedTariff",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.cost),
title: "Custo",
key: "orderCost",
sorter: true,
align: "right",
},
{
render: (render) => formatCurrency(render.revenue),
title: "Receita",
key: "orderRevenue",
sorter: true,
align: "right",
},
{
title: "Parceiro",
name: "Parceiro",
dataIndex: ["partner", "name"],
key: "orderPartnerName",
sorter: true,
align: "center",
...getColumnSearchProps(
"orderPartnerName",
"select",
"multiple",
partners,
getPartners)
},
{
title: "id da transação",
name: "id da transação",
dataIndex: "id",
},
];
useEffect(function transactions() {
async function fetchTransactions() {
const response = await getTransactions(context, paginationValues);
if (response) {
const { data, pagination } = response;
setData(data);
setPaginationValues(pagination);
}
}
fetchTransactions();
// eslint-disable-next-line
}, []);
return <Table dataSource={data} columns={columns} />;
};
export default Insider;
You could move this piece of code
const [filters, setFilters] = useState([]);
In a higher level
I have a pie chart implemented with VictoryPie. I would like to display a label when the mouse hovers over each slice.
I put a print statement inside the MouseFollowToolTip component. When the page loads, the statement has printed five times, which makes sense as there are five slices. Then when I move my mouse around the pie, it continues to print.
However, at no point does it display the label associated with the slice.
import React from 'react';
import Grid from '$components/grid';
import { VictoryLegend, VictoryPie, VictoryTooltip, VictoryLabel, Selection } from 'victory';
const creditScoreMakeup = [
{
x: 'Payment history',
y: 35,
label:
'The best way for you to improve \nyour credit score is to \nfocus on making payments on time.',
},
{
x: 'Credit utilization',
y: 30,
label:
'You should try to carry little \nto no balance on your credit card \nto lower your credit utilization and \nimprove your credit score.',
},
{
x: 'Length of history',
y: 15,
label:
'A longer credit history provides lenders with more information about how you use credit which helps them predict how you will behave financially long-term.',
},
{
x: 'Credit diversity',
y: 10,
label:
'Having a mix of loans and credit cards \ngives lenders the impression that you can \nhandle various forms of credit responsibly.',
},
{
x: 'Credit inquiries',
y: 10,
label:
'Having many inquiries in a \nshort period of time in your \ncredit history suggests that you \nare in financial trouble and need \na significant amount of money.',
},
];
class MouseFollowToolTip extends React.Component {
static defaultEvents = [
{
target: "data",
eventHandlers: {
onMouseOver: evt => {
const { x, y } = Selection.getSVGEventCoordinates(evt);
return {
mutation: () => ({
target: "labels",
x,
y,
active: true
})
};
},
onMouseMove: evt => {
const { x, y } = Selection.getSVGEventCoordinates(evt);
return {
mutation: () => ({
target: "labels",
x,
y,
active: true
})
};
},
onMouseOut: () => {
return { target: "labels", mutation: () => ({ active: false }) };
}
}
}
];
render() {
console.log("called")
return <VictoryTooltip {...this.props} pointerLength={0} renderInPortal={false} />;
}
}
class CreditScore extends React.Component {
render() {
return (
<svg width={1000} height={1000}>
<VictoryLegend
standalone={false}
renderInPortal={false}
colorScale="green"
legendWidth={50}
x={20}
y={40}
gutter={20}
title="Legend"
centerTitle
style={{ border: { stroke: 'black' } }}
data= {creditScoreMakeup.map((a, ind) => {
return { name: a.x };
})}
/>
<VictoryPie
colorScale="green"
data={creditScoreMakeup}
standalone={false}
renderInPortal={false}
width={800}
height={400}
padding-right={100}
style={{ parent: { maxWidth: '50%' } }}
//labels={d => `${d.label}%`}
labelComponent= {<MouseFollowToolTip/>}
/>
</svg>
);
}
}
I've made a sandbox here
Here is a solution that tracks the mouse movement inside the CreditScore Component:
class MouseFollowTooltip extends VictoryTooltip {
render() {
return (
<VictoryTooltip
{...this.props}
pointerLength={16}
renderInPortal={false}
/>
);
}
}
class CreditScore extends React.Component {
state = {
x: 0,
y: 0
};
updateCoords = e => {
this.setState({ x: e.clientX, y: e.clientY });
};
render() {
return (
<svg onMouseMove={this.updateCoords} width={1000} height={1000}>
<VictoryLegend
standalone={false}
renderInPortal={false}
colorScale="green"
legendWidth={50}
x={20}
y={40}
gutter={20}
title="Legend"
centerTitle
style={{ border: { stroke: "black" } }}
data={creditScoreMakeup.map((a, ind) => {
return { name: a.x };
})}
/>
<VictoryPie
colorScale="green"
data={creditScoreMakeup}
standalone={false}
renderInPortal={false}
width={800}
height={400}
padding-right={100}
style={{ parent: { maxWidth: "50%" } }}
labels={d => `${d.label}%`}
labelComponent={
<MouseFollowTooltip x={this.state.x} y={this.state.y} />
}
/>
</svg>
);
}
}
Codesandbox: https://codesandbox.io/s/muddy-forest-6n4fp