I am just starting to learn coding so please bear with me. Could you help me solve the following problem?
I am trying to pass a state from one child component to another child component, I am using react-typist, once typing is done, I would like to change the style on my other component, which is my navigation bar. I believe I should lift the state up, but I cannot make it work.
Here is my code:
import Typist from 'react-typist';
class Intro extends Component {
constructor (props) {
super(props);
this.state = {
navStatus: 'back'
};
this.onIntroTyped=this.onIntroTyped.bind(this);
}
onIntroTyped(front){
this.setState({ navStatus: 'front'});
}
render() {
return (
<Typist avgTypingDelay={10}
startDelay={0}
onTypingDone={this.onIntroTyped}
navStatus={this.state.navStatus}
onIntroTyped={this.onIntroTyped}>
<div class="text">
.....
</div>
</Typist>
);
}
}
export default Intro;
class Nav extends Component {
constructor(props) {
super(props);
this.state = {
id: "slider",
navStatus: "back"
};
}
onMenuClick = (event) => {
if (this.state.id === "slider"){
this.setState({id: "sliderOut"});
}
else (this.setState({id: "slider"}));
}
render () {
return (
<nav id="navigation" className={"navStatus"}>
<div class="links">
<a href="#" onClick={this.onMenuClick}>Menu</a>
...
</div>
</nav>
);
}
}
export default Nav;
Moved state up to a wrapper parent component (Container component) called Test in this case.
The state "navStatus" is shared among its children's and the value is available to each children via props.
Also when a child want to change the value of that shared state it calls a method supplied by parent in this case it is onIntroTyped.
State of parent gets changed and all child elements gets rendered because of that and they have updated shared value via props.
Just for illustration purpose, see below example
class Test extends React.Component {
constructor(){
super();
this.state = {
navStatus: "back"
};
this.onIntroTyped=this.onIntroTyped.bind(this);
}
onIntroTyped(){
this.setState({ navStatus: 'front'});
}
render() {
return (
<div>
<Nav navStatus={this.state.navStatus} />
<Intro onIntroTyped={this.onIntroTyped} />
</div>
);
}
}
class Intro extends Component {
constructor(props) {
super(props);
}
render() {
return (
<button onClick={this.props.onIntroTyped}> test Intro typed </button>
);
}
}
class Nav extends Component {
constructor(props) {
super(props);
this.state = {
id: "slider",
};
}
onMenuClick = (event) => {
if (this.state.id === "slider"){
this.setState({id: "sliderOut"});
}
else this.setState({id: "slider"});
}
render () {
return (
<nav id="navigation" className={this.props.navStatus}>
<div class="links">
<a href="#" onClick={this.onMenuClick}>Menu</a>
...
</div>
</nav>
);
}
}
ReactDOM.render( <Test /> ,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Related
I have parent component like below:
<template>
<button #click="initStr" value="init str" />
<child :str="str" />
</template>
<script>
export default {
components: { child, },
setup() {
const str= ref("");
function initStr() {
str.value = "init";
}
return {
str,
initStr,
};
}
};
</script>
The problem is when click the button on parent to init string, the child component does not rerender with the new string. I have to create another ref varible in child component then watch the props to assign the new string like below:
const string = ref(props.str);
watch(props, props => {
string.value = props.str;
});
Is this the only way to rerender the child when props from parent changes?
It should work as shown in the following example :
const {
createApp
} = Vue;
const App = {
setup() {
const str = Vue.ref("");
function initStr() {
str.value = "init";
}
return {
str,
initStr,
};
}
}
const app = createApp(App)
app.component('child', {
props: ['str'],
template: `
<div> str : {{str}}</div>
`
})
app.mount('#app')
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app">
<button #click="initStr">
init str
</button>
<child :str="str" />
</div>
Can I use a child components state in App.js? I mean, in a child component I have a variable for example i = 5 and I would like to use this in App.js. In App.js this.state.i shows 0.
This is a very basic pattern in react so maybe just read through the official docs oder do some tutorials for getting started with react.
But to help you out on this you need to pass down a function to your child that sets the state in <App/>
So something like this:
App:
this.state { i : 0 }
updateState() {
this.setState(i: i + 1); // or whatever
}
render() {
return (
<>
<Child updateState={this.updateState} />
</>
....
Child:
<div onClick={props.updateState}>Click me</div>
On app.js do this;
constructor(props) {
this.updateI=this.updateI.bind(this)
super(props)
this.state = {
i:0
}
}
updateI(i){
this.setState({i})
}
render() {
return (
<View >
<Child updateI={this.updateI}/>
</View>
)
}
on your child do this:
this.props.updateI(5);
I am following this question's first answer to create a common parent for two of my components
import React, {Component} from 'react';
import ButtonSubmit from './ButtonSubmit'
import Form from './Form'
export default class ParentofButtonandForm extends Component {
constructor() {
super();
this.state = {
username: '',
password : '',
};
}
changeFirst(receivedUN,reaceivedPW) {
this.setState({
username: receivedUN,
password:reaceivedPW
});
}
render() {
return (
<Form username={this.state.username} password={this.state.password} changeFirst={this.changeFirst.bind(this)}/>
<ButtonSubmit username={this.state.username} password={this.state.password}/>
)
}
}
But i get unrechable code error in
<ButtonSubmit username={this.state.username} password={this.state.password}/>
I dont know what i am doing wrong. I also get a ':expected' warning in this.state.username.
You are returning two components from render functions. Either you wrap <Form> and <Button> into another component, may be View OR you can return a component array from render function.
Wrapping inside View
render() {
return (
<View>
<Form .../>
<ButtonSubmit .../>
</View>
)
}
Returning array of components, link
render() {
return [
<Form .../>,
<ButtonSubmit .../>
];
}
Hope this will help!
Refer to the code below, currently all the children were rendered inside the default slot even though the slot name is given.
Not sure whether vue createElement function supports named slot?
#Component({
props:[]
})
export class TestComponent extends Widget{
items:any[];
render(h:any){
const rootcmp = {
template:`<div>
Temp:<slot name="temp"></slot>
Default:<slot></slot>
</div>`
, data:()=>{
return {};
}
}
const cmp = {
template:'<div slot="default">This is child</div>'
, data:()=>{
return {};
}
}
const cmp2 = {
template:'<div slot="temp">This is child</div>'
, data:()=>{
return {};
}
}
return h(rootcmp, [h(cmp), h(cmp2)]);
}
}
Current behavior:
<div>
Temp:Default:
<div>This is child</div>
<div>This is child</div>
</div>
Expected behavior:
<div>
Temp:
<div>This is child</div>
Default:
<div>This is child</div>
</div>
It sure does, in your options object, try {slot:'slot-name'}
Assuming the following and all the components/fus/fci/ssg have just a single h1 with a site props. I want to understand why it is a valid react element yet these are not showing equally rendered. That is one has the h1 element and the other doesn't. The idea was to not create large component with toggles for different sites and each site would be swapped out based on the nav pick. I don't see anything documented for this unless I missed it...
{this.state.renderSite}
<Fci site="Fci"/>
import React from 'react';
import styles from './App.css';
import Nav from '../components/Nav.js'
import Fus from '../components/Fus.js'
import Fci from '../components/Fci.js'
import Ssg from '../components/Ssg.js'
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {renderSite: '', site: 'default' };
this.pickSite = this.pickSite.bind(this);
}
pickSite(site){
this.setState({renderSite: React.createElement(site, {"site":site})});
this.setState({site: site});
console.log( React.isValidElement(this.state.renderSite));
}
render() {
return (
<div className={styles.app}>
<Nav site={this.pickSite.bind(this)} />
{this.state.renderSite}
<Fci site="Fci"/>
</div>
);
}
}
The Nav
import React from 'react';
export default class Nav extends React.Component {
constructor(props) {
super(props);
this.update = this.update.bind(this);
}
update(e) {
this.props.site(e.target.dataset.site);
}
render(){
return (
<div>
<button onClick={this.update} data-site="Ssg"> SSG </button>
<button onClick={this.update} data-site="Fci"> FCI </button>
<button onClick={this.update} data-site="Fus"> FUS </button>
</div>
);
}
}
The problem is when you create the element you are passing a string (data-site value), not a component reference. So it ends up like this:
React.createElement("Fci");
As opposed to:
React.createElement(Fci);
Using a string will create a simple HTML element, not a component with with its own rendered content.
You could create a component map like this:
const componentMap = {
"Fci": Fci,
"Fus": Fus,
"Ssg": Ssg
}
Then from your string you can resolve a component reference:
React.createElement(componentMap[site], {site: site});
Or you could pass a component reference from your Nav:
<button onClick={this.update.bind(this, Ssg, "Ssg"}> SSG </button>
update(component, site, e) {
this.props.site(component, site);
}
pickSite(component, site) {
React.createElement(component, {site: site});
}