2018年5月13日 星期日

組件組合、pubsub-js (React 16 三)

如上圖,分成三個組件,最外面的組件包括兩個組件
在文字輸入一些文字,然後按增加,就會到 radio button 的上面


※先寫靜態

class App extends React.Component {
    constructor(p) {
        super(p)
    }
    
    render() {
        return ( 
            <div>
                <h1>Component 組合練習</h1>
                <Add />
                <RadioList />
            </div>
        )
    }
}
    
class Add extends React.Component {
    constructor(p) {
        super(p)
        this.handleAdd = this.handleAdd.bind(this)
    }
    
    handleAdd() {
        const txt = this.txt.value.trim();
        if(!txt){
            return
        }
    }
    
    render() {
        return (
            <div>
                <input ref={i => this.txt = i}/> 
                <button onClick={this.handleAdd}>增加#5</button>
            </div>
        )
    }
}
    
const xxx = ["a", "b", "c"];
class RadioList extends React.Component {
    constructor(p) {
        super(p)
    }
    
    render() {
        return (
            <ul>
                {
                    xxx.map((data, i) => <li key={i}><input type="radio" name="x" />{data}</li>)
                }
            </ul>
        )
    }
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));



※再寫動態

class App extends React.Component {
    constructor(p) {
        super(p)
        this.state = {
            data:["a", "b", "c"]
        }
    }
    
    render() {
        return ( 
            <div>
                <h1>Component 組合練習</h1>
                <Add count={this.state.data.length} />
                <RadioList data={this.state.data} />
            </div>
        )
    }
}
    
class Add extends React.Component {
    constructor(p) {
        super(p)
        this.handleAdd = this.handleAdd.bind(this)
    }
    
    handleAdd() {
        const txt = this.txt.value.trim()
        if(!txt) {
            return
        }
        console.log(txt)
    }
    
    render() {
        return (
            <div>
                <input ref={i => this.txt = i}/> 
                <button onClick={this.handleAdd}>增加#{this.props.count + 1}</button>
            </div>
        )
    }
}
    
Add.propTypes = {
    count : PropTypes.number.isRequired
}
    
// const xxx = ["a", "b", "c"];
class RadioList extends React.Component {
    constructor(p) {
        super(p)
    }
    
    render() {
        return (
            <ul>
                {
                    this.props.data.map((data, i) => {
                        return <li key={i}><input type="radio" name="x" />{data}</li>
                    })
                }
            </ul>
        )
    }
}
    
RadioList.propTypes = {
    data : PropTypes.array.isRequired
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));



※互動

class App extends React.Component {
    constructor(p) {
        super(p)
        this.state = {
            data:["a", "b", "c"]
        }
        this.addNewData = this.addNewData.bind(this)
    }
    
    addNewData(newData) {
        this.state.data.unshift(newData)
        this.setState({data : this.state.data})
    }
    
    render() {
        return ( 
            <div>
                <h1>Component 組合練習</h1>
                <Add count={this.state.data.length} addNewData={this.addNewData} />
                <RadioList data={this.state.data} />
            </div>
        )
    }
}
    
class Add extends React.Component {
    constructor(p) {
        super(p)
        this.handleAdd = this.handleAdd.bind(this)
    }
    
    handleAdd() {
        const txt = this.txt.value.trim()
        if(!txt) {
            return
        }
        this.props.addNewData(txt)
        this.txt.value = ''
    }
    
    render() {
        return (
            <div>
                <input ref={i => this.txt = i}/> 
                <button onClick={this.handleAdd}>增加#{this.props.count + 1}</button>
            </div>
        )
    }
}
    
Add.propTypes = {
    count : PropTypes.number.isRequired,
    addNewData : PropTypes.func.isRequired
}
    
class RadioList extends React.Component {
    constructor(p) {
        super(p)
    }
    
    render() {
        return (
            <ul>
                {
                    this.props.data.map((data, i) => {
                        return <li key={i}><input type="radio" name="x" />{data}</li>
                    })
                }
            </ul>
        )
    }
}
    
RadioList.propTypes = {
    data : PropTypes.array.isRequired
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));

※由於子組件不能使用 this.state 改變父組件的狀態
而狀態在哪一個組件,this.state 就寫在哪
所以傳一個 function 給子組件調用



※簡化並整理代碼

class App extends React.Component {
    state = {
        data:["a", "b", "c"]
    }
    
    addNewData = (newData) => {
        const {data} = this.state
        data.unshift(newData)
        this.setState({data})
    }
    
    render() {
        const {data} = this.state;
        return ( 
            <div>
                <h1>Component 組合練習</h1>
                <Add count={data.length} addNewData={this.addNewData} />
                <RadioList data={data} />
            </div>
        )
    }
}
    
class Add extends React.Component {
    static propTypes = {
        count : PropTypes.number.isRequired,
        addNewData : PropTypes.func.isRequired
    }
    
    handleAdd = _ => {
        const txt = this.txt.value.trim()
        if(!txt) {
            return
        }
        this.props.addNewData(txt)
        this.txt.value = ''
    }
    
    render() {
        return (
            <div>
                <input ref={i => this.txt = i}/> 
                <button onClick={this.handleAdd}>
                    增加#{this.props.count + 1}
                </button>
            </div>
        )
    }
}
    
class RadioList extends React.Component {
    static propTypes = {
        data : PropTypes.array.isRequired
    }
    
    render() {
        const {data} = this.props
        return (
            <ul>
                {
                    data.map((data, i) => {
                        return (
                            <li key={i}>
                                <input type="radio" name="x" />
                                {data}
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));

※使用箭頭函數不用 bind

※this.state 寫在外面,是因為繼承了 React.Component 才可以這麼做,ES6 不可以

※propTypes 寫在類別裡

※修改成解構賦值,以下是原理:
const {a} = {a:1}
console.log(a); // 1
    
const {b, c} = {b:2, c:3}
console.log(b, c); // 2, 3
    
    
const ooo = {d:4}
const {d} = ooo;
console.log(d); // 4
    
const xxx = {e:5, f:6}
const {e, f} = xxx;
console.log(e, f); // 5, 6

※ooo 和 xxx 就好比是 this.state



※pubsub-js

可以在全域宣告,可以解決上面的傳 func 的問題,父子間、兄弟間傳參都很麻煩,可以用這種辦法解決,官網連結
www.bootcdn.cn 的下載點:https://cdn.bootcss.com/pubsub-js/1.6.0/pubsub.js
使用 Node.js:到專案路徑下,執行 yarn add pubsub-js
執行完可下 npm list pubsub-js 查看是否成功
然後使用 import PubSub from 'pubsub-js'; 即可



<title>PubSub練習</title>
<script src="./js/development_16.3.2.js"></script>
<script src="./js/dom.development_16.3.2.js"></script>
<script src="./js/babel_7.0.0-beta.3.js"></script>
<script src="pubsub_1.6.0.js"></script>
    
    
    
<div id="div1"></div>
<script type="text/babel">
class App extends React.Component {
    declare = _ => {
        PubSub.subscribe('xxx', (p1, p2) => {
            console.log("p1=" + p1, "p2=" + p2) // p1=xxx p2=xxxxx
        });
    }
    
    trigger = _ => {
        PubSub.publish('xxx', 'xxxxx');
    }
    
    noTrigger = _ => {
        PubSub.unsubscribe('xxx');
    }
    
    render() {
        return (
            <div>
                <h1>PubSub 練習</h1>
                <button onClick={this.declare}>宣告</button>
                <button onClick={this.trigger}>觸發</button>
                <button onClick={this.noTrigger}>不想再觸發</button>
            </div>
        )
    }
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));
</script>

※還有很多小技巧,自行參考官網


※再次將組件簡化後的代碼

<script src="./js/development_16.3.2.js"></script>
<script src="./js/dom.development_16.3.2.js"></script>
<script src="./js/babel_7.0.0-beta.3.js"></script>
<script src="prop-types.js"></script>
<script src="pubsub_1.6.0.js"></script>
    
    
    
<div id="div1"></div>
<script type="text/babel">
class App extends React.Component {
    state = {
        data:["a", "b", "c"],
        declareXxx: _ => {
            PubSub.subscribe('xxx', (p1, newData) => {
                const {data} = this.state
                data.unshift(newData)
                this.setState({data})
            });
        }
    }
    
    render() {
        this.state.declareXxx();
        const {data} = this.state;
        return (
            <div>
                <h1>Component 組合練習</h1>
                <Add count={data.length} />
                <RadioList data={data} />
            </div>
        )
    }
}
    
class Add extends React.Component {
    static propTypes = {
        count : PropTypes.number.isRequired,
    }
    
    handleAdd = _ => {
        const txt = this.txt.value.trim()
        if(!txt) {
            return
        }
        console.log(txt)
        PubSub.publish('xxx', txt);
        this.txt.value = ''
    }
    
    render() {
        return (
            <div>
                <input ref={i => this.txt = i}/> 
                <button onClick={this.handleAdd}>
                    增加#{this.props.count + 1}
                </button>
            </div>
        )
    }
}
    
class RadioList extends React.Component {
    static propTypes = {
        data : PropTypes.array.isRequired
    }
    
    render() {
        const {data} = this.props
        return (
            <ul>
                {
                    data.map((data, i) => {
                        return (
                            <li key={i}>
                                <input type="radio" name="x" />
                                {data}
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}
    
ReactDOM.render(<App />, document.querySelector("#div1"));
</script>



沒有留言:

張貼留言