const backgroundSvgs: ReactNode[] = returnArrayHtmlElements(svgs, 50);
return (<div>
{backgroundSvgs.map((e) => {
e.style.x = '50px' <-- How can I do this?
})}
</div>);
I have variable backgroundSvgs, which is of type ReactNode[]; how can I access its style before rendering it?
You can use React.Children.map or React.cloneElement.
React.Children.map:
function MyComponent(props) {
return React.Children.map(props.children, child => {
// Modificação dos estilos do componente filho
const newChild = React.cloneElement(child, { style: { color: 'red' } });
return newChild;
});
}
React.cloneElement:
function MyComponent(props) {
return React.cloneElement(props.children, { style: { color: 'red' } });
}
Related
I am developing an accordion which is nested in character. I mean, an accordion can have its child accordion as well. Right now, a simple accordion has been build up. The toggling part works either. However, there is a problem. Child accordion is shown without opening the parent accordion. How can i show child accordion only when parent accordion is clicked and hide it when clicked again?
Here is what i have done
class Accordion extends React.Component {
constructor(props) {
super(props);
let state = { activeSections: {} };
React.Children.toArray(props.children).forEach(child => {
if (child) {
state.activeSections[child.props.name] = !!child.props.defaultOpen;
}
});
this.state = state;
}
get isControlled() {
return typeof this.props.onToggle === "function";
}
expandAll = () => {
if (this.isControlled) {
this.props.expandAll();
} else {
let { activeSections } = this.state;
Object.keys(activeSections).forEach(k => (activeSections[k] = true));
this.setState({ activeSections });
}
};
collapseAll = () => {
if (this.isControlled) {
this.props.collapseAll();
} else {
let { activeSections } = this.state;
Object.keys(activeSections).forEach(k => (activeSections[k] = false));
this.setState({ activeSections });
}
};
onToggle = name => {
if (this.isControlled) {
this.props.onToggle(name);
} else {
let activeSections = this.state.activeSections;
this.setState({
activeSections: { ...activeSections, [name]: !activeSections[name] }
});
}
};
componentWillReceiveProps(nextProps) {
let { activeSections } = this.state;
React.Children.toArray(nextProps.children)
.filter(c => c)
.forEach(child => {
if (activeSections[child.props.name] == null) {
activeSections[child.props.name] = !!child.props.defaultOpen;
}
});
this.setState({ activeSections });
}
render() {
let { activeSections } = this.state;
let children = React.Children.toArray(this.props.children);
// if (!children.find(c => c && c.type === AccordionHeader)) {
// children = [<AccordionHeader />, ...children];
// }
console.log("children", children);
return (
<div>
{children.map(child => {
if (!child) {
return child;
} else if (child.type === AccordionItem) {
return React.cloneElement(child, {
expanded: this.isControlled
? child.props.expanded
: activeSections[child.props.name],
onToggle: this.onToggle
});
} else {
return child;
}
})}
</div>
);
}
}
export default Accordion;
class AccordionItem extends React.Component {
render() {
let {
expanded,
caption,
onToggle,
name,
children,
render,
...rest
} = this.props;
return render ? (
render({ onToggle: onToggle.bind(null, name), expanded })
) : (
<styled.AccordionItem style={{ margin: 10 }}>
<styled.AccordionHeader
onClick={() => onToggle(name)}
active={expanded}
>
{caption}
</styled.AccordionHeader>
<styled.AccordionBody active={rest.defaultOpen || expanded}>
{children && (
<styled.AccordionBodyContent>
{children}
</styled.AccordionBodyContent>
)}
</styled.AccordionBody>
</styled.AccordionItem>
);
}
}
export default AccordionItem;
I have create a working example in the sandbox and here it is
https://codesandbox.io/s/z25j8q2v4m
In my React code, I'm using Babel's stage 3 class properties, which doesn't need a constructor for declaring states and event listeners. The parent component has two states: color and listener. The problem now is, the this.state.listener, which is passed as clickEvent prop, is NOT working.
The code below is supposed to change the color of the button from white to black and vice versa on every click.
const Button = props => {
const { background, clickEvent } = props;
const styles = {
background,
color: background === '#000' ? '#fff' : '#000'
};
return <button style={styles} onClick={clickEvent}>Change color</button>
}
export default class App extends Component {
state = {
color: '#fff',
listener: this.changeColor
}
changeColor = () => {
this.setState(state => ({
color: state.color === '#000' ? '#fff' : '#000'
}))
}
render() {
const { color, listener } = this.state;
return (
<Button background={color} clickEvent={listener} />
)
}
}
You should not store functions in your state, juste variables. You should pass the changeColor functions to the child props like this:
const Button = props => {
const { background, clickEvent } = props;
const styles = {
background,
color: background === '#000' ? '#fff' : '#000'
};
return <button style={styles} onClick={clickEvent}>Change color</button>
}
export default class App extends Component {
state = {
color: '#fff',
}
changeColor = () => {
this.setState(state => ({
color: state.color === '#000' ? '#fff' : '#000'
}))
}
render() {
const { color } = this.state;
return (
<Button background={color} clickEvent={this.changeColor} />
)
}
}
Just directly use that method, like so:
export default class App extends Component {
state = {
color: '#fff'
}
changeColor = () => {
this.setState(state => ({
color: state.color === '#000' ? '#fff' : '#000'
}))
}
render() {
const { color, listener } = this.state;
return (
<Button background={color} clickEvent={this.changeColor} /> //fixed
)
}
}
We don't need to make it a state property.
The reason it isn't working is you're doing things in the wrong order. You declare this.state first, and this.changeColor later, so you're trying to access this.changeColor before it is defined. So if you wanted to continue storing it in state, which i agree with others is probably unnecessary, you will need to swap their order:
export default class App extends Component {
changeColor = () => {
this.setState(state => ({
color: state.color === '#000' ? '#fff' : '#000'
}))
}
state = {
color: '#fff',
listener: this.changeColor
}
render() {
const { color, listener } = this.state;
return (
<Button background={color} clickEvent={listener} />
)
}
}
here is code, why my map function not return item in div.
I have use array of object in state function.
Here is my simple code.
I have XML data in componentwiillrecieveprops. is there any issue by componentwillmount. I do not understand why map function in map my array of state.
import React from 'react';
import TextField from 'material-ui/TextField';
var self;
export default class NewAuthoring extends React.Component {
constructor(props) {
super(props);
self = this;
this.state = {
sampleState : "OriginalState",
task : [
{event:"First data",eventpara:"First Data"},
{event:"Second data",eventpara:"Second Data"},
{event:"Third data",eventpara:"Third Data"}
]
}
}
componentWillReceiveProps(nextProps) {
console.log(nextProps.xml)
if(this.props != nextProps) {
//Do Something when any props recieve
this.setState({sampleState:nextProps.xml});
}
}
componentWillMount() {
//Do something before component renders
let xml ="<div type=”timeline/slideshow”><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section></div>";
self.props.getChildXml(xml);
}
componentDidMount() {
//Do Something when component is mounted
}
handleChange(e) {
//getChildXml function will update the xml with the given
//parameter and will also change the xml dialog value
let xml ="<div type=”timeline/slideshow”><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section><section><header></header><article></article></section></div>";
self.props.getChildXml(xml);
}
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
}
}
Any help will be appreciated.
You are not returning element from map callback. Also i see that tasks is an array of object, and you are directly rendering object by writting {item}. You need to return the element and should avoid rendering object directly like this
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
Alternatively you can also avoid use of {} brackets to return the element without writting return keyword.
{
this.state.task.map((item,contentIndex) => (
<div>
hello
{item.event}
{item.eventpara}
</div>
))
}
UPDATE: You need to return something from render function
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
return (
<div>
{
this.state.task.map((item,contentIndex) => {
return (<div>
hello
{item.event}
{item.eventpara}
</div>)
})
}
</div>
)
}
Since the map pattern is very common in React you could also do something like this:
1: Create a Map/Iterator component
const Iterator = (props) => {
//you could validate proptypes also...
if(!props.array.length) return null
return props.array.map(element => props.component(element))
}
2.return it passing the component as props:
render() {
const myStyle = {
mainBlock:{
fontWeight:"bold",
margin:"2px"
}
}
const div_style = {
border:'1px solid black',
margin:10
}
return <Iterator
array={this.state.task}
component={
item=>(<div key={item.event}>hello{item.event}
{item.eventpara} } </div>
/>
}
Because you're returning nothing in render(). Use it as follows:
render(){
return(
{this.state.task.map((item,contentIndex) => {
return <SomeElement />;
}
);
}
In my TextInput field I called onChange action if text has been changed. It works very fine with redux.
But i need to add more actions:
onFocuse (if text payload === '0', then i need to change TextInput value to '')
onBlur (if text payload === '', then i need to change TextInput value to '0')
I don't have any idea. For example JQuery decision:
function addEventOnChange(obj){
jQuery('#'+obj).bind('keyup mouseup change',function(e){
change(document.getElementById(obj));
});
jQuery('#'+obj).click(
function(){//focusin
clears(document.getElementById(obj));
});
jQuery('#'+obj).blur(
function(){//focusout
backzero(document.getElementById(obj));
});
function clears(obj) {
if (obj.value == 0) {
obj.value = '';
}
}
function backzero(obj) {
if (obj.value == "") {
obj.value = 0;
}
}
My current action:
export const textInputChanged = (text) => {
return {
type: TEXTINPUT_CHANGED,
payload: text
};
};
Current reducer:
const INITIAL_STATE = {
textinput: '1000'
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case TEXTINPUT_CHANGED:
return { ...state, textinput: action.payload };
default:
return state;
}
};
Current App:
onTextInputChange(text) {
this.props.TextInputChanged(number(text));
}
render() {
const number = (text) => { // only for numbers input
if (text.match(',') !== null) {
text = text.replace(',', '.');
}
if (text.match(/[*.*][0-9]*[*.*]/) !== null) {
if (text.match(/\.$/)) {
text = text.replace(/\.$/, '');
} else {
text = text.replace(/[.]/, '');
}
}
return text.replace(/[^\d.]/g, '');
};
return (
<Card>
<TextInput
value={this.props.texinput}
onChangeText={this.onTextInputChange.bind(this)}
onFocus={ clears }
onBlur={ backzero }
/>
</Card>
const mapStateToProps = (state) => {
return {
textinput: state.form.textinput,
};
};
export default connect(mapStateToProps, {
TextInputChanged
})(App);
Decision smells like componentDidMount(), but I don't feel as well
It's working. Somewhere may be a more elegant solution. You can add TextInput2, TextInput3, etc. by the same principle in this onFocus && onBlur functions. It turned out without eval)
Current App:
class App extends Component {
constructor() {
super();
this.state = {
TextInputColor: '#525050'
};
}
onFocus(input, text) {
this.setState({
[`${input}Color`]: '#000000'
});
if (text === '0') {
this.props[`${input}Changed`]('');
}
}
onBlur(input, text) {
this.setState({
[`${input}Color`]: '#525050'
});
if (text === '') {
this.props[`${input}Changed`]('0');
}
}
onTextInputChange(text) {
this.props.TextInputChanged(number(text));
}
render() {
const number = (text) => { // only for numbers input };
return (
<Card>
<TextInput
value={this.props.texinput}
onChangeText={this.onTextInputChange.bind(this)}
onBlur={() => this.onBlur('TextInput', this.props.textinput)}
onFocus={() => this.onFocus('TextInput',this.props.textinput)}
style={{ color: this.state.TextInputColor }}
/>
</Card>
const mapStateToProps = (state) => {
return {
textinput: state.form.textinput,
};
};
export default connect(mapStateToProps, {
TextInputChanged
})(App);
Gif
I think this is what you're looking for. It was also the first result from google. Just an FYI..
Focus style for TextInput in react-native
Update....
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={{ height:60, backgroundColor: this.state.backgroundColor, color: this.state.color }} />
onFocus() {
this.setState({
backgroundColor: 'green'
})
},
onBlur() {
this.setState({
backgroundColor: '#ededed'
})
},
I can't write all the code for you... You will need to setup your code.
handleChange(e){
//check if text is what you need then call some function
//e === 0 ? this.onBlur (or this.setState({...})) : null
}
I have a react.js component where I want to pass down a bunch of different methods to child components from the parent component, the methods modify the state of the parent component.
class Page extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items,
currentItemID: 1
};
this.actions = this.actions.bind(this);
}
render() {
return (
<div className="page">
{
this.state.items.map(item =>
<Item key={item._id} item={item} actions={this.actions} />
)
}
</div>
);
}
actions() {
return {
insertItem: function (itemID) {
const currentItems = this.state.items;
const itemPosition = this.state.items.map((item) => item._id).indexOf(itemID);
const blankItem = {
_id: (new Date().getTime()),
content: ''
};
currentItems.splice(itemPosition + 1, 0, blankItem)
this.setState({
items: currentItems,
lastAddedItemID: blankItem._id
});
},
setCurrentItem: function (itemID) {
this.setState({ currentItemID: itemID });
},
focus: function(itemID) {
return (itemID === this.state.currentItemID);
}
}
}
In my child component, I am trying to use the focus method in the componentDidMount lifecyle method as shown below:
componentDidMount() {
if (this.props.actions().focus(this.props.item._id)) {
this.nameInput.focus();
}
}
However, I am getting the error
Uncaught TypeError: Cannot read property 'currentItemID' of undefined
in the definition of the focus method, within the actions methods. Can anyone point me in the right direction as to why I'm getting the error or an alternative way to pass down multiple actions to child components?
the context is not passed to the function, then the 'this' in the function is that of the function itself and not the component.. you can solve it that way (put the functions in the components):
actions() {
return {
insertItem: this.insertItem.bind(this),
setCurrentItem: this.setCurrentItem.bind(this),
focus: this.focus.bind(this),
}
}
insertItem(itemID) {
const currentItems = this.state.items;
const itemPosition = this.state.items.map((item) => item._id).indexOf(itemID);
const blankItem = {
_id: (new Date().getTime()),
content: ''
};
currentItems.splice(itemPosition + 1, 0, blankItem)
this.setState({
items: currentItems,
lastAddedItemID: blankItem._id
});
},
setCurrentItem(itemID) {
this.setState({ currentItemID: itemID });
},
focus(itemID) {
return (itemID === this.state.currentItemID);
}
but yet, the recomended way is to put the functions in the components like above and remove the actions method and do this:
<Item key={item._id} item={item} actions={{
insertItem: this.insertItem.bind(this),
setCurrentItem: this.setCurrentItem.bind(this),
focus: this.focus.bind(this)
}} />
or
<Item key={item._id} item={item} actions={{
insertItem: () => this.insertItem(),
setCurrentItem: () => this.setCurrentItem(),
focus: () => this.focus()
}} />