Redux的行为不正确

时间:2018-05-03 14:14:29

标签: javascript reactjs redux

我刚刚学到了一些react-redux并且遇到了我无法理解的问题并且至少坚持了4天。

  1. 首先出现问题,可以在检查员控制台看到(我使用的是Chrome)。 我在反应组件内的<div>处有事件处理程序。它必须在onClick事件中调用,但它会在每次加载或重新加载站点时触发。

  2. 其次,站在减速机功能附近。它告诉我在控制台(开发工具)中减速器接收到的动作&#39; TOGGLE_TILE&#39;并返回undefined而不是object。应该注意到reducer成功接收状态,动作属性并在里面进行一些操作,但结果没有正常的返回。

    我的reducer,actions,main,container,presentation组件和函数的代码提供。请尽量回答扩展,我想了解什么是错误的,不要在代码中犯两次错误。

  3. 也!我使用redux-thunk中间件(对于动作中的功能回调,你知道)。 在里面我有:

    index.js - 主要组件

    const store = createStore(reducer, applyMiddleware(thunk));
    
    ReactDOM.render(
        <Provider store={store}>
            <AppContainer />
        </Provider>, 
        document.getElementById('root')
    );
    registerServiceWorker();
    

    actions.js

    export function toggle(id){
        return{
            type: 'TOGGLE_TILE',
            id
        };
    }
    
    export function toggleTile(id){
        return dispatch => {
            console.log('toggling');
            dispatch(toggle(id));
        };
    }
    

    tiles.js - Reducer

    var i = 0;
    
    function tiles(state = tilesContainer, action){
        var openedTiles = [];
        switch (action.type) {
            case 'TOGGLE_TILE':
                if(i < 2){  
                    console.log('i: '+i);  
                    state.map((value) => {
                        var newOpen;
                        if(!value.opened && action.id === value.id){  
                            newOpen = Object.assign({}, value, {
                                opened: !value.opened
                            });  
                            openedTiles.push(newOpen);
                            i++;
                            console.log(i, value.opened, newOpen, openedTiles);
                        }
                        return newOpen, i;
                    });
                }else if(i === 2){
                    var curr, prev;
                    openedTiles.map((value) => {
                        if(!prev){
                            prev = value;
                        }else{
                            curr = value;
                            console.log("Prev and curr: "+prev, curr);
                            if(curr.name === prev.name){
                                var currRes = Object.assign({}, curr, {
                                    disappeared: !curr.disappeared
                                });
                                var prevRes = Object.assign({}, prev, {
                                    disappeared: !prev.disappeared
                                });
                                return {currRes, prevRes}; 
                            } else {
                                let currRes = Object.assign({}, curr, {
                                    opened: !curr.opened
                                });
                                let prevRes = Object.assign({}, prev, {
                                    opened: !prev.opened
                                })
                                return currRes, prevRes;
                            }
                        }
                    });
                }else{
                    return state;
                }
            default:
                return state;
        }
        console.log("tiles: "+state.forEach(value => console.log(value)));
    }
    
    
    
    const reducers = combineReducers({
        tiles
    });
    
    export default reducers;
    

    AppContainer.jsx

    const mapStateToProps = (state) => {
      return {
        tiles: state.tiles
      };
    };
    
    const mapDispatchToProps = (dispatch) => {
      return {
        toggle: id => {
          // console.log(id);
          dispatch(toggleTile(id));
        }
      };
    };
    
    
    class AppContainer extends Component {
      constructor(props){
        super(props);
      }
      componentDidMount(){
    
      }
      render() {
        var prop = this.props;
        console.log(prop);
        return (
          <div>
            <AppView prop={prop} />
          </div>
        );
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);
    

    AppView.js

    class AppView extends React.Component {
        constructor(props){
            super(props);
            this.state = {
                tiles: this.props.prop.tiles,
            };
            this.showTiles = this.showTiles.bind(this);
            this.defineRatio = this.defineRatio.bind(this);
            this.toggleTile = this.toggleTile.bind(this);
        }
        componentDidMount(){
            this.defineRatio();
    
        }
        componentWillMount(){
    
        }
        defineRatio(){
            var imgClass;
            let tile = document.querySelectorAll('img');
            tile.forEach((value) => {
                var imgSrc, imgW, imgH;
                function defineImage(imgSrc){
                    var img = new Image();
                    img.src = imgSrc;
                    img.onload = function() {   
                        return {
                            src:imgSrc,
                            width:this.width,
                            height:this.height};
                        };
                    return img;
                }
                var x = defineImage(value.src);
                x.addEventListener('load',function(){
                    imgSrc = x.src;
                    imgW = x.width;
                    imgH = x.height;
                    // console.log(value.src, imgW, imgH);
                    var imgClass = (imgW / imgH > 1) ? 'wide' : 'tall';
                    value.classList += imgClass;
                });
            });
        }
        toggleTile(id){
            this.props.prop.toggle(id);
        }
        showTiles(){
            const boxElems = this.state.tiles.map((value, index) => {
                var styles = {background: 'black'};
                var tileState = value.opened ? '' : styles;
                var imgState = value.opened ? 'opened ' : 'closed ';
                var elem = <img key={value.id} src={value.src} alt="" className={imgState} />;
                var boxElem = <div style={tileState} className="tile-box " onClick={this.toggleTile(value.id)} key={index}>{elem}</div>;
                return boxElem;
            });
            return boxElems;
        }
        render(){
            var tiles = this.showTiles();
            return (
                <div className="tiles-box">
                    <div className="tiles">
                        {tiles}
                    </div>
                </div>
            );
        }
    }
    
    export default AppView;
    

2 个答案:

答案 0 :(得分:1)

第一个问题可以通过替换

来解决

onClick={this.toggleTile(value.id)} with onClick={(e) => this.toggleTile(value.id)}第一个语句只是立即调用this.toggleTile(value.id)并将返回值设置为OnClick事件。

关于秒,你没有从你的reducer返回任何东西,因此状态是未定义的。

       if(i < 2){  
            console.log('i: '+i);  
            state.map((value) => {
                var newOpen;
                if(!value.opened && action.id === value.id){  
                    newOpen = Object.assign({}, value, {
                        opened: !value.opened
                    });  
                    openedTiles.push(newOpen);
                    i++;
                    console.log(i, value.opened, newOpen, openedTiles);
                }
                return newOpen, i;
            });
        }

这个return newOpen, i应该是return newOpen是什么,因为这个返回是在map函数中你必须返回映射数组 所以使用return state.map((value) => {

答案 1 :(得分:1)

您遇到的问题是您实际上是在div内调用该功能,因此每次进入视图时都会触发该功能,因此请替换showTiles()上的以下代码

var boxElem = <div style={tileState} className="tile-box " onClick={this.toggleTile(value.id)} key={index}>{elem}</div>;

到此:

var boxElem = <div style={tileState} className="tile-box " onClick={e => this.toggleTile(value.id)} key={index}>{elem}</div>;

实际上这应该可以解决第2点的错误。