reactjs前端實踐|第三篇:TodoList示例事件、state、props、refs

實踐三

Todo List示例,未使用redux。內容涉及到展現組件與容器組件的合理使用,事件處理,參數傳遞,控件訪問,組件功能設計等方面。其中遇到的坎,設置輸入焦點,因爲使用了styled-components而有不一樣;每一個組件應試包含哪些state,是否應該進行狀態提高;子組件如何向父組件傳遞數據。node

代碼分析

組件拆分:Li單個信息條目、LiList信息條目列表、InputAndButton輸入組件、App容器組件。App的State包含一個item數組,存放信息列表;InputAndButton的State包含一個inputValue變量,存入用戶本次輸入的信息。其它組件不含State,爲展現組件。react

展現組件

Li.js,展現一條信息,數據來自父組件git

const Li = (props) => {
    return (                           
        <TodoLi>{props.text}</TodoLi>              //此處不能加key(但父組件要向它傳遞),不然會有警告
    );
};
export default Li;

LiList.js,展現信息列表,由於輸入組件也會修改數據,因此state提高到容器組件中。github

const LiList = (props)=>{
    return (
        <TodoUl>
            {props.items.map((item)=>{             //map方法來完成列表構建
                return <Li {...item}></Li>;        //item中要包含一個key字段,不然會有警告
            })}
        </TodoUl>
    );
};
export default LiList;

輸入組件

輸入組件本身控制輸入框的數據顯示及清除,開始我把一切事件處理全放到App中了,這樣方便了數據傳遞,但破壞了輸入組件的內聚性。正確的方式是:輸入組件本身控制界面的顯示,只有在回車鍵按下或鼠標點擊add按鈕時,纔將輸入框中的數據傳遞給父組件(App)。npm

InputAndButton.jsredux

class InputAndButton extends Component {
    constructor(props)    {
        super(props);
        this.state={
            inputValue:''                      //用來處理鼠標點擊時取得輸入數據
        };
    }
    onChange = e => {
        this.setState({
            inputValue:e.target.value         //按鍵時保存輸入數據
        })
    };
    addItem = e => {
        if(e.which === 13 || e.type === 'click'){    //回車或鼠標點擊
            if(this.state.inputValue.trim().length > 0) {     //輸入不爲空
                this.props.onSave(this.state.inputValue.trim());   //調用父組件提供的函數,向父組件傳遞數據。
                e.target.value = '';     //清空輸入
                this.setState({          
                    inputValue:''
                })
            }
        }
    };
    render = () => {
        return (
            <div>
                <TodoInput onChange={this.onChange}
                           onKeyUp={this.addItem}
                           placeholder="enter task"
                           value={this.state.inputValue}>
                </TodoInput>
                <TodoButton onClick={this.addItem} type="submit">add</TodoButton>
            </div>
        );
    }
}
export default InputAndButton;

容器組件

class App extends Component {
    constructor(props){
        super(props);
        this.state={
            items:[]                   //信息列表數據
        };
    }
    handleSave(text){                 //傳遞到子組件的函數,接收數據並追加條目
        if(text.length !== 0){
            this.state.items.push({key: new Date().getTime(), text});     //key設定惟一值,不做說明,網上資料不少
            this.setState({
                items: this.state.items,
            })
        }
    };
    render() {
        return (
            <div>
                <InputAndButton onSave={this.handleSave.bind(this)}/>                     //處理函數以props傳給子組件
                <LiList items={this.state.items}/>
            </div>
        );
    };
}
export default App;

輸入焦點控制

利用特殊的屬性 Ref來訪問子組件中的控件,並控制輸入焦點,須要注意的是:styled-components對這個屬性有些改變,要用innerRef來替代ref,否則拿到的是一個 StyledComponent包裝的對象,而不是DOM結點。有疑問,去看其文檔的Tips and tricks部分之Refs to DOM nodes。數組

App.js中輸入組件修改以下:函數

<InputAndButton ref={comp => { this.InputComponent = comp; }} onSave={this.handleSave.bind(this)}/>

App.js中增長componentDidMount處理,在組件加載完成時設置輸入焦點。this

componentDidMount (){
        this.InputComponent.focus();
    };

InputAndButton.js中修改input以下:.net

<TodoInput innerRef={el=> { this.el = el; }}   ...

InputAndButton.js中增長函數,供父組件中調用。

focus = () => {
        this.el.focus();
    };

項目地址

https://git.oschina.net/zhoutk/reactodo.git
https://github.com/zhoutk/reactodo.git

使用方法

git clone https://git.oschina.net/zhoutk/reactodo.git
or git clone https://github.com/zhoutk/reactodo.git
cd reactodo
npm i
git checkout todo_list_react
npm start

小結

此次實踐,學會了事件,數據傳遞,組件劃分,功能設計等基本方法,能夠去折騰redux了。

相關文章
相關標籤/搜索